Skip to content

Conversation

@empovit
Copy link
Member

@empovit empovit commented Jan 8, 2026

  • Update Console SDK 0.0.11 → 1.4.0
  • Upgrade PatternFly v4 → v6
  • Upgrade to Node.js 20
  • Enable TypeScript strict mode
  • Update CSS and UI controls
  • Bump Helm chart version to 0.3.0 (backward compatible)
  • Update dependencies

Summary by CodeRabbit

  • New Features

    • Automated i18n tooling and English-defaults; new Overview detail component; redesigned GPU selector and simplified dashboard layout; major GPU‑focused translation update.
  • Bug Fixes

    • Fixed pod links/footer rendering, disabled loading/no‑GPU selector options, corrected workload table wrapping.
  • Documentation

    • Updated prerequisites (OpenShift 4.19+), README, dependency guidance, and chart version.
  • Style

    • New CSS utilities, centered metric titles, and refreshed "Not available" styling.
  • Chores

    • Modernized build, linting, webpack, container configs, .dockerignore, and package metadata.

✏️ Tip: You can customize this high-level summary in your review settings.

@openshift-ci
Copy link

openshift-ci bot commented Jan 8, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: empovit

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci bot added the approved label Jan 8, 2026
@coderabbitai
Copy link

coderabbitai bot commented Jan 8, 2026

Walkthrough

Consolidates linting/style configs, upgrades Node/tooling and webpack, adds an i18n extraction pipeline (scripts, lexer, parser config), introduces a shared OverviewDetailItem component and styles, tightens TypeScript, adjusts UI/CSS and Docker files, and updates localization and dependency metadata.

Changes

Cohort / File(s) Summary
Linting / Formatting
/.eslintrc (deleted), .eslintrc.yml, /.prettierrc.yml
Old .eslintrc removed; new .eslintrc.yml added for TypeScript/React + Prettier; some Prettier keys removed.
Stylelint & ignore files
.stylelintrc.yaml, /.gitignore, /.dockerignore
Added Stylelint config tuned for PatternFly; added .dockerignore; added .cache_* to .gitignore.
Docker build
Dockerfile
Base image updated to ubi9/nodejs-20; WORKDIR/COPY paths changed; ensures yarn available; multi-stage copy path updated.
Toolchain & bundler
package.json, webpack.config.ts, tsconfig.json
Added license/engines, script rewrites to webpack, tightened TS (strict, sourceMap), production-aware webpack (hashing, SCSS, devServer), and multiple dependency/version updates.
i18n pipeline
i18n-scripts/*, i18next-parser.config.js
Added build-i18n.sh, common.js, lexers.js (CustomJSONLexer), set-english-defaults.js; i18next parser config updated to use the custom JSON lexer and sorting.
Localization content
locales/en/plugin__console-plugin-nvidia-gpu.json
Major migration to GPU-centric localization keys: many keys added/renamed/removed and reorganized.
Shared component & styles
src/components/shared/OverviewDetailItem.tsx, src/components/shared/OverviewDetailItem.css
New OverviewDetailItem React component with props type and corresponding CSS.
Import/path updates
src/components/ClusterOverview/GPUProviders.tsx, src/components/GPUDashboard/Cards/InfoCard.tsx
Switched imports to local OverviewDetailItem path.
UI / CSS tweaks
src/components/GPUDashboard/**/*.css, src/components/NotAvailable.css, src/components/GPUDashboard/Cards/WorkloadsCard.tsx, src/components/GPUDashboard/Cards/MetricsCard.tsx, src/components/GPUDashboard/GPUStatus.tsx, src/components/NotAvailable.tsx
Added .ng-break-word, .ng-metrics-card__title, .ng-not-available; replaced some token usage with CSS variables; updated EmptyState icon and column class names.
Dashboard / selector / layout
src/components/GPUDashboard/GPUSelector.tsx, src/components/GPUDashboard/GPUDashboard.tsx
GPUSelector refactored to controlled SelectList + MenuToggle; GPUDashboard outer wrappers simplified.
PodStatus adjustments
src/components/PodStatus/PodStatus.tsx
Replaced Text* components with Content, corrected pod name path segments in links, and simplified footer markup.
Utilities
src/utils/units.ts
Humanize functions now accept `string
Docs & metadata
README.md, deployment/console-plugin-nvidia-gpu/Chart.yaml, DEPENDENCIES.md
Bumped Chart version and OCP compatibility to 4.19+, updated README, and added DEPENDENCIES.md documenting pinned deps.
Misc
other small edits
Minor CSS/JS fixes, import adjustments, and removal of the old .eslintrc.

Sequence Diagram(s)

sequenceDiagram
  actor Dev/CI
  participant Shell as ./i18n-scripts/build-i18n.sh
  participant Parser as i18next-parser
  participant Lexer as CustomJSONLexer
  participant FS as filesystem (src + locales)
  participant Node as set-english-defaults.js

  Dev/CI->>Shell: run build-i18n.sh
  Shell->>Parser: invoke i18next-parser with pattern & config
  Parser->>Lexer: request extraction for JSON/comment-JSON files
  Lexer->>Lexer: parse comment-JSON & collect %KEY% tokens
  Lexer-->>Parser: return extracted keys
  Parser->>FS: write locales/$LOCALE/$NAMESPACE.json
  Dev/CI->>Node: run set-english-defaults.js
  Node->>FS: read locales/en/*, update empty values, write back
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

lgtm

Suggested reviewers

  • fabiendupont
  • TomerNewman
  • yakovbeder
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the primary objective of the PR: upgrading the plugin to support OpenShift 4.19+ compatibility, which aligns with the major version bumps (SDK, PatternFly, Node.js) and configuration changes throughout the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@empovit empovit changed the title Upgrade plugin for OpenShift 4.19+ compatibility [WIP] Upgrade plugin for OpenShift 4.19+ compatibility Jan 8, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/GPUDashboard/GPUSelector.tsx (1)

24-31: Prevent selection of loading and error states.

The loading and error state options can still be selected by users, which would set invalid values (like "Loading GPUs" or "No GPU found") as the selected GPU UUID. These options should be explicitly disabled to prevent selection.

🐛 Proposed fix to disable loading/error options
  if (!gpusLoaded) {
    items = [
-      <SelectOption key="disabled" value={t('Loading GPUs')}>
+      <SelectOption key="disabled" value={t('Loading GPUs')} isDisabled>
        <Spinner size="md" /> {t('Loading GPUs')}
      </SelectOption>,
    ];
  } else if (!gpus?.length || gpusError) {
-    items = [<SelectOption key="disabled" value={t('No GPU found')} />];
+    items = [<SelectOption key="disabled" value={t('No GPU found')} isDisabled />];
  } else {
🧹 Nitpick comments (10)
src/utils/units.ts (1)

209-217: Cache the numeric conversion to avoid repeated Number(v) calls.

The function calls Number(v) three times. For consistency with humanizeRatio and humanizePercentage (which use numValue), cache the conversion result.

♻️ Suggested refactor
 export const humanizeCpuCores: HumanizeFunc = (v) => {
-  const value = Number(v) < 1 ? round(Number(v) * 1000) : Number(v);
-  const unit = Number(v) < 1 ? 'm' : '';
+  const numValue = Number(v);
+  const value = numValue < 1 ? round(numValue * 1000) : numValue;
+  const unit = numValue < 1 ? 'm' : '';
   return {
     string: `${formatValue(value)}${unit}`,
     unit,
     value,
   };
 };
src/components/PodStatus/PodStatus.tsx (1)

116-133: Consider consistent paragraph styling.

The informational text uses <Content component="p"> (lines 117-124) while the links section uses plain <p> (lines 125-133). For consistency, consider applying the same approach throughout, unless the distinction is intentional to differentiate content types.

♻️ Proposed refactor for consistency
-          <p>
+          <Content component="p">
             <Link to={`/k8s/ns/${pod.metadata.namespace}/pods/${pod.metadata.name}/logs`}>
               {t('View logs')}
             </Link>
             &emsp;
             <Link to={`/k8s/ns/${pod.metadata.namespace}/pods/${pod.metadata.name}/events`}>
               {t('View events')}
             </Link>
-          </p>
+          </Content>
src/components/shared/OverviewDetailItem.tsx (1)

33-33: Consider avoiding trailing space in className.

The current template literal will produce a trailing space when valueClassName is undefined or empty. While harmless, it's slightly cleaner to avoid it.

♻️ Optional refactor to remove trailing space
-      <dd className={`ng-overview-detail-item__value ${valueClassName || ''}`}>{value}</dd>
+      <dd className={valueClassName ? `ng-overview-detail-item__value ${valueClassName}` : 'ng-overview-detail-item__value'}>{value}</dd>

Alternative using array approach:

-      <dd className={`ng-overview-detail-item__value ${valueClassName || ''}`}>{value}</dd>
+      <dd className={['ng-overview-detail-item__value', valueClassName].filter(Boolean).join(' ')}>{value}</dd>
src/components/GPUDashboard/GPUSelector.tsx (1)

33-35: Consider displaying GPU name instead of UUID.

Line 54 displays selectedGPU?.uuid in the MenuToggle, which shows a UUID to users. Consider displaying a more user-friendly GPU name or model if available in the GPU object.

♻️ Optional enhancement for better UX

If the gpu object has a name or modelName property, consider:

  } else {
    items = gpus.map((gpu) => (
-      <SelectOption key={gpu.uuid} value={gpu.uuid} description={`Model: ${gpu.modelName}`} />
+      <SelectOption key={gpu.uuid} value={gpu.uuid}>
+        {gpu.modelName || gpu.uuid}
+      </SelectOption>
    ));
  }

And update the toggle display:

        >
-          {selectedGPU?.uuid || t('Select GPU')}
+          {selectedGPU?.modelName || selectedGPU?.uuid || t('Select GPU')}
        </MenuToggle>
i18n-scripts/lexers.js (1)

1-30: CustomJSONLexer implementation looks correct.

The lexer correctly:

  • Uses comment-json to parse JSON with comments support
  • Employs a reviver function to extract localization keys matching the %KEY% pattern
  • Handles errors gracefully by logging and returning an empty array
  • Returns keys in the expected { key: ... } format for i18next-parser

The class extends EventEmitter but doesn't emit events. This is likely for interface compatibility with i18next-parser lexer expectations, though it could be removed if not required by the framework.

♻️ Optional: Remove EventEmitter if not required by framework

If i18next-parser doesn't require lexers to extend EventEmitter, you can simplify:

-const EventEmitter = require('events');
 const jsonc = require('comment-json');

 /**
  * Custom JSON parser for localizing keys matching format: /%.+%/
  */
-module.exports.CustomJSONLexer = class extends EventEmitter {
+module.exports.CustomJSONLexer = class {
   extract(content, filename) {
i18next-parser.config.js (1)

1-4: Consider configuring ESLint environment instead of inline disables.

The multiple eslint-disable comments work but suggest ESLint isn't configured for Node.js CommonJS files. Consider adding a .eslintrc override for .js files or setting env: { node: true } to properly recognize require and module.

♻️ Alternative: Configure ESLint for Node.js environment

In your ESLint configuration, add an override for .js files:

{
  "overrides": [
    {
      "files": ["*.js"],
      "env": {
        "node": true
      },
      "rules": {
        "@typescript-eslint/no-var-requires": "off"
      }
    }
  ]
}

This would eliminate the need for inline disable comments.

webpack.config.ts (1)

8-8: Mixed import styles: consider standardizing.

The file uses ES6 imports throughout except for CopyWebpackPlugin. If possible, standardize to ES6 imports for consistency.

♻️ Standardize to ES6 import
-const CopyWebpackPlugin = require('copy-webpack-plugin');
+import CopyWebpackPlugin from 'copy-webpack-plugin';

Note: Only apply if compatible with your TypeScript configuration and plugin version.

i18n-scripts/common.js (2)

14-26: Consider propagating errors to callers.

Errors are logged but the Promise doesn't reject, preventing callers from detecting failures. For build scripts, propagating errors allows proper failure handling.

♻️ Propagate errors to enable proper failure handling
 parseFolder(directory, argFunction, packageDir) {
   return (async () => {
     try {
       const files = await fs.promises.readdir(directory);
       for (const file of files) {
         const filePath = path.join(directory, file);
         await argFunction(filePath, packageDir);
       }
     } catch (e) {
       console.error(`Failed to parseFolder ${directory}:`, e);
+      throw e;
     }
   })();
 },

This allows callers to handle failures appropriately, such as exiting with a non-zero status in build scripts.


27-33: Consider propagating errors for consistency.

Like parseFolder, errors are logged but not thrown. For consistency and to allow callers to handle failures, consider propagating errors.

♻️ Optional: propagate errors
 deleteFile(filePath) {
   try {
     fs.unlinkSync(filePath);
   } catch (e) {
     console.error(`Failed to delete file ${filePath}:`, e);
+    throw e;
   }
 },

Note: Only apply if file deletion failures should halt the build process.

i18n-scripts/set-english-defaults.js (1)

74-76: Consider adding error exit for build failures.

If locale processing fails, the script continues without exiting with a non-zero status. Build scripts should typically fail explicitly to prevent incomplete builds.

♻️ Exit with error status on failure
 (async () => {
-  await common.parseFolder(publicDir, processLocalesFolder);
+  try {
+    await common.parseFolder(publicDir, processLocalesFolder);
+  } catch (error) {
+    console.error('Failed to set English defaults:', error);
+    process.exit(1);
+  }
 })();

This requires updating common.js to propagate errors (see previous comment on that file).

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d74eb74 and bd88e0f.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (29)
  • .eslintrc
  • .eslintrc.yml
  • .gitignore
  • .prettierrc.yml
  • .stylelintrc.yaml
  • Dockerfile
  • README.md
  • deployment/console-plugin-nvidia-gpu/Chart.yaml
  • i18n-scripts/build-i18n.sh
  • i18n-scripts/common.js
  • i18n-scripts/lexers.js
  • i18n-scripts/set-english-defaults.js
  • i18next-parser.config.js
  • package.json
  • src/components/ClusterOverview/GPUProviders.tsx
  • src/components/ClusterOverview/GPUStatus.tsx
  • src/components/GPUDashboard/Cards/InfoCard.tsx
  • src/components/GPUDashboard/Cards/MetricsCard.tsx
  • src/components/GPUDashboard/Cards/WorkloadsCard.css
  • src/components/GPUDashboard/Cards/WorkloadsCard.tsx
  • src/components/GPUDashboard/GPUDashboard.tsx
  • src/components/GPUDashboard/GPUSelector.tsx
  • src/components/NotAvailable.tsx
  • src/components/PodStatus/PodStatus.tsx
  • src/components/shared/OverviewDetailItem.css
  • src/components/shared/OverviewDetailItem.tsx
  • src/utils/units.ts
  • tsconfig.json
  • webpack.config.ts
💤 Files with no reviewable changes (1)
  • .eslintrc
🧰 Additional context used
🧬 Code graph analysis (3)
i18n-scripts/lexers.js (2)
i18next-parser.config.js (1)
  • require (2-2)
i18n-scripts/set-english-defaults.js (1)
  • keys (34-34)
src/components/GPUDashboard/GPUDashboard.tsx (1)
src/components/GPUDashboard/Cards/GPUDashboardGraphs.tsx (1)
  • GPUDashboardGraphs (48-146)
i18n-scripts/set-english-defaults.js (3)
i18n-scripts/common.js (2)
  • fs (1-1)
  • path (2-2)
i18next-parser.config.js (1)
  • require (2-2)
i18n-scripts/lexers.js (1)
  • keys (9-9)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: lint-test
🔇 Additional comments (46)
src/components/NotAvailable.tsx (1)

8-8: LGTM! Correct PatternFly v6 class migration.

The CSS class update from pf-u-color-200 to pf-v6-u-color-200 correctly follows PatternFly v6 naming conventions. The component functionality remains unchanged.

src/components/GPUDashboard/Cards/MetricsCard.tsx (1)

95-95: LGTM! PatternFly v6 utility class update is correct.

The class name update from pf-u-text-align-center to pf-v6-u-text-align-center is consistent with the PatternFly v4 → v6 upgrade described in the PR objectives.

src/utils/units.ts (4)

179-187: LGTM!

The type broadening to accept string | number aligns with the SDK's Humanize type and accommodates string values commonly returned from Prometheus queries.


189-208: LGTM!

Consistent coercion pattern applied across all helpers. The internal humanize function already handles non-finite values, so invalid string inputs gracefully fall back to 0.


219-232: LGTM!

Clean implementation with proper handling of non-finite values and -0. The numValue caching pattern is consistent and efficient.


234-245: LGTM!

Consistent implementation pattern with humanizeRatio. Proper handling of edge cases.

src/components/PodStatus/PodStatus.tsx (2)

3-3: LGTM! PatternFly v6 migration applied correctly.

The addition of Content component and removal of deprecated Text components aligns with the PatternFly v6 upgrade mentioned in the PR objectives.


126-126: Excellent catch! Critical typo fixed.

The correction from pod.metadata.nam to pod.metadata.name fixes broken URLs that would have prevented navigation to pod logs, events, and debug container pages.

Also applies to: 130-130, 140-140

src/components/GPUDashboard/Cards/InfoCard.tsx (1)

3-3: LGTM! Import path updated correctly.

The import has been successfully migrated from the external package to the local shared module. The usage throughout the file is consistent with the new component's API.

src/components/ClusterOverview/GPUProviders.tsx (1)

2-2: LGTM! Import path correctly updated.

The migration from the external package to the local shared module is consistent and correct.

src/components/GPUDashboard/GPUDashboard.tsx (1)

26-55: LGTM! Layout simplified appropriately.

The removal of the Page wrapper and nested PageSection simplifies the component structure while preserving all functionality. The internal layout hierarchy and component composition remain intact.

src/components/shared/OverviewDetailItem.css (1)

1-18: LGTM! CSS follows PatternFly v6 conventions.

The stylesheet correctly:

  • Uses PatternFly v6 design tokens for consistency
  • Follows the ng- prefix naming convention as specified in PR objectives
  • Implements proper text wrapping (lines 16-17) to handle long content gracefully
  • Provides appropriate semantic styling for the dl/dt/dd structure
src/components/shared/OverviewDetailItem.tsx (1)

1-36: LGTM! Well-structured component with good semantic HTML.

The component implementation is solid:

  • Props interface is clean and matches all usage patterns
  • Semantic dl/dt/dd HTML is appropriate for key-value pairs
  • Loading/error states are properly handled with PatternFly v6 components
  • CSS module integration follows best practices
src/components/GPUDashboard/Cards/WorkloadsCard.css (1)

4-9: LGTM! CSS naming convention correctly applied.

The changes properly implement the ng- prefix naming convention mentioned in the PR objectives. The .ng-break-word class with word-break and overflow-wrap properties is appropriate for handling long text content in table cells.

src/components/GPUDashboard/Cards/WorkloadsCard.tsx (2)

49-54: LGTM! CSS class names consistently updated.

All table column class names have been consistently updated from co-break-word to ng-break-word, correctly following the ng- prefix naming convention and matching the CSS file changes.


42-42: PatternFly v6 EmptyState icon prop usage is correct.

The icon={CubesIcon} prop is the correct PatternFly v6 API. The EmptyStateIcon component is no longer directly imported—it's now internal to EmptyState and automatically wraps the icon component you provide.

src/components/ClusterOverview/GPUStatus.tsx (2)

115-115: LGTM! Icon color migration looks correct.

The updated usage of okColor directly (removing .value) aligns with the PatternFly v4→v6 migration. PatternFly v6 icons should properly handle CSS variable strings in the color prop.

Also applies to: 150-150


18-19: The CSS variable and fallback color are correct. The PatternFly v6 token --pf-t--global--color--status--success--default and the fallback color #3D7317 match PatternFly v6 documentation. No centralized token constants file exists in the codebase, so the hardcoded approach is consistent with project conventions.

src/components/GPUDashboard/GPUSelector.tsx (4)

22-22: Good type refinement for strict mode.

The more specific React.ReactElement<SelectOptionProps>[] typing aligns well with TypeScript strict mode and provides better type safety.


47-56: MenuToggle implementation looks correct.

The custom toggle implementation with MenuToggle properly manages refs, click handlers, and accessibility attributes (isExpanded, isDisabled).


2-10: The PatternFly v6 Select imports and implementation are correct. All imported components (SelectOption, SelectList, SelectOptionProps, Select, MenuToggle, MenuToggleElement) are valid exports from @patternfly/react-core and are used properly in the composable Select pattern with correct type safety and handlers.


42-45: The type assertion is safe in this context.

The as string assertion on line 43 is not actually unsafe. All SelectOption values used in this component are strings (both the translated strings on lines 26 and 31, and the GPU UUIDs on line 34). More importantly, the Select component is disabled (via isDisabled on line 20) when the loading and error options are shown, so the onSelect handler is never invoked with those translated strings—only with actual GPU UUID strings. PatternFly v6's onSelect signature accepts string | number, and in this case, only strings are used. The type assertion is redundant but not a safety concern.

Likely an incorrect or invalid review comment.

.gitignore (1)

77-77: Pattern addition looks good.

The .cache_* addition extends parcel-bundler cache ignores to cover dot-variant naming patterns, which aligns with the updated build environment.

.prettierrc.yml (1)

5-6: Verify Prettier config simplification doesn't change formatting behavior.

The removed options (tabWidth, semi, jsxSingleQuote, proseWrap) may have defaults in Prettier that differ from the previous explicit values. Confirm that relying on Prettier's defaults for these options won't unexpectedly change formatting behavior across the codebase.

.eslintrc.yml (1)

1-26: ESLint config is well-structured for TypeScript + React.

The configuration appropriately layers ESLint recommendations, React and TypeScript plugins, and Prettier integration. Disabling react/prop-types in favor of TypeScript's type checking and using auto-detect for React version are modern best practices.

One note: ecmaVersion: latest (line 13) will automatically pick up new ECMAScript versions as ESLint/parser support evolves. For strict reproducibility, consider pinning to a specific version (e.g., 2024). However, given this PR's modernization scope, using latest is likely intentional.

i18n-scripts/build-i18n.sh (1)

1-7: LGTM! Script follows shell best practices.

The script correctly uses strict error handling (set -exuo pipefail) and properly escapes the i18next template variables (\$LOCALE, \$NAMESPACE) to prevent shell expansion. The file pattern and command invocation are appropriate for the i18n extraction workflow.

Dockerfile (2)

1-1: Upgrade to UBI9 and Node.js 20 aligns with PR objectives.

The base image upgrade from ubi8/nodejs-16 to ubi9/nodejs-20 correctly implements the Node.js 20 upgrade mentioned in the PR objectives.


5-11: Build paths correctly updated for new working directory.

The change from root directory to /usr/src/app as the working directory is a good practice, and the artifact copy path in line 11 (--from=build /usr/src/app/dist) correctly reflects this new structure.

tsconfig.json (2)

7-12: TypeScript strict mode enabled as per PR objectives.

The addition of strict: true (line 10) correctly implements the PR objective to enable TypeScript strict mode. The sourceMap: true (line 7) aids debugging, and skipLibCheck: true (line 12) is a common practice to improve compilation performance.


16-22: ts-node configuration supports new i18n tooling.

The ts-node block enables in-project TypeScript execution with CommonJS module resolution, which aligns with the new i18n-scripts infrastructure introduced in this PR. The transpileOnly: true setting is appropriate for build scripts where faster compilation is prioritized.

i18next-parser.config.js (2)

6-6: Good addition: Catalog sorting improves consistency.

Enabling sort: true ensures translation keys are consistently ordered in the generated catalog files, making diffs cleaner and improving maintainability.


15-30: Mixed lexer reference pattern is correct and follows i18next-parser convention.

The lexers configuration correctly uses string references for built-in lexers ('HandlebarsLexer', 'HTMLLexer', 'JavascriptLexer') and a direct class reference for the custom lexer (CustomJSONLexer). This is the documented and intended pattern for i18next-parser: pass string names for built-in lexers and pass the class/function directly when using custom lexers.

.stylelintrc.yaml (1)

1-27: LGTM! Well-structured Stylelint configuration for PatternFly v6.

The configuration appropriately enforces dark mode compatibility and plugin CSS isolation. The rules correctly prevent:

  • Direct color values (enforcing PF CSS variables)
  • Reserved prefixes (.pf-, .co-)
  • Naked element selectors that could impact console layout

The comments clearly explain the rationale for each rule.

webpack.config.ts (5)

43-44: LGTM! SCSS loader chain correctly configured.

The loader chain (sass-loader → css-loader → style-loader) follows webpack best practices for SCSS processing.


53-58: Good addition for module compatibility.

Setting fullySpecified: false improves compatibility with modules that don't use full file extensions in imports, which is a common webpack 5 migration pattern.


64-69: LGTM! DevServer configuration appropriate for development.

The permissive allowedHosts: 'all' and CORS headers are acceptable since they only apply to the development server, not production builds.


76-78: LGTM! CopyWebpackPlugin correctly configured for locale files.

The plugin will copy locale files from the source locales directory to dist/locales, which aligns with the i18n infrastructure added in this PR.


80-84: LGTM! Production optimization settings properly configured.

The configuration appropriately toggles between development (source maps, named chunks) and production (no source maps, deterministic chunks, minification) modes.

package.json (4)

5-8: LGTM! Node.js 20 requirement aligns with modern tooling.

The Node.js 20 engine requirement is appropriate for the updated dependencies and aligns with the Dockerfile upgrade mentioned in the PR objectives.


9-20: LGTM! Script updates align with modernized tooling.

The updated scripts properly support:

  • Production builds with explicit NODE_ENV
  • TypeScript configuration execution via ts-node
  • Integrated i18n workflow
  • CSS linting with stylelint

84-86: LGTM! Correct peerDependency declaration for plugin architecture.

Declaring i18next as a peerDependency is appropriate for a console plugin, ensuring the host console provides a single i18next instance.


22-63: No action needed. PatternFly v6 (including 6.2.2) officially supports React 17 and follows a "two most recent React versions" policy. OpenShift Console SDK 1.4.0 is designed for React 17.x, making this the intended combination. The dependency versions are compatible.

i18n-scripts/common.js (1)

5-13: LGTM! Synchronous operation acceptable for build scripts.

Using lstatSync is appropriate for build-time scripts where blocking is acceptable. The error handling correctly returns false for non-existent paths.

i18n-scripts/set-english-defaults.js (3)

8-19: LGTM! Rule determination follows i18next conventions.

The priority order (WithCount_plural → WithCount → _plural → default) correctly matches i18next naming patterns. The use of includes() is appropriate given standard i18next key conventions.


21-66: LGTM! Transformation logic correctly implements i18next conventions.

The function properly:

  • Validates file structure
  • Preserves existing translations (only fills empty strings)
  • Applies appropriate transformations based on key naming patterns
  • Uses the pluralize library for English pluralization

The file overwriting behavior is acceptable for a build script.


68-72: LGTM! Correctly targets only English locale for defaults.

Filtering by basename === 'en' ensures the script only modifies English locale files, leaving other locales for human translators.

@empovit empovit force-pushed the update-for-ocp-4.19-no-chart-changes branch from bd88e0f to 6107e33 Compare January 13, 2026 14:30
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/GPUDashboard/GPUSelector.tsx (1)

25-31: Add isDisabled to placeholder SelectOptions to prevent selection.

The loading and "No GPU found" options are selectable, which could cause unexpected behavior if a user clicks them. These placeholder items should be disabled.

Proposed fix
   if (!gpusLoaded) {
     items = [
-      <SelectOption key="disabled" value={t('Loading GPUs')}>
+      <SelectOption key="disabled" value={t('Loading GPUs')} isDisabled>
         <Spinner size="md" /> {t('Loading GPUs')}
       </SelectOption>,
     ];
   } else if (!gpus?.length || gpusError) {
-    items = [<SelectOption key="disabled" value={t('No GPU found')} />];
+    items = [<SelectOption key="disabled" value={t('No GPU found')} isDisabled />];
   } else {
🤖 Fix all issues with AI agents
In @deployment/console-plugin-nvidia-gpu/Chart.yaml:
- Line 4: The Chart.yaml metadata still lists version: 0.2.7 while the PR
updates the OpenShift requirement to 4.19+; update the Chart.yaml "version"
field to 0.3.0 to reflect this breaking change (and search the chart for any
other occurrences of the old chart version to update them as well); do not
change the descriptive text you already updated, just bump the Chart.yaml
version entry (and ensure any release notes or changelog entries, if present,
reflect the version bump).

In @package.json:
- Around line 24-27: The dependency @patternfly/react-charts is pinned to v7
which targets PatternFly 5; update the package version string
"@patternfly/react-charts" to use the v8 semver range (e.g., ^8.x) so it aligns
with the PatternFly 6 packages (@patternfly/react-core, @patternfly/react-icons,
@patternfly/react-table); update package.json accordingly and run
install/lockfile update to ensure dependency resolution and tests pass.
- Line 61: The package.json currently pins webpack to the vulnerable version
"5.75.0"; update the dependency entry for "webpack" to a fixed release (at
minimum ">=5.76.0", preferably "5.94.0" or the latest 5.x) and then regenerate
your lockfile (npm install / yarn install) so the new version is installed;
after updating, run the test suite and a security audit (npm audit / yarn audit)
to verify the vulnerability is resolved.
🧹 Nitpick comments (6)
src/components/GPUDashboard/GPUSelector.tsx (1)

33-35: Consider displaying GPU model name instead of UUID for better UX.

The SelectOption has no children, so PF v6 displays value (the UUID) as the option label. Combined with the MenuToggle also showing selectedGPU?.uuid, users see raw UUIDs rather than human-readable GPU names.

Proposed improvement
   } else {
     items = gpus.map((gpu) => (
-      <SelectOption key={gpu.uuid} value={gpu.uuid} description={`Model: ${gpu.modelName}`} />
+      <SelectOption key={gpu.uuid} value={gpu.uuid} description={gpu.uuid}>
+        {gpu.modelName}
+      </SelectOption>
     ));
   }

And update the toggle display:

         <MenuToggle
           ref={toggleRef}
           onClick={() => setOpen(!isOpen)}
           isExpanded={isOpen}
           isDisabled={isDisabled}
         >
-          {selectedGPU?.uuid || t('Select GPU')}
+          {selectedGPU?.modelName || t('Select GPU')}
         </MenuToggle>

Also applies to: 54-54

.prettierrc.yml (1)

1-6: LGTM - Config simplification looks good.

The removal of redundant keys (tabWidth, semi, jsxSingleQuote, proseWrap) in favor of Prettier's sensible defaults is a reasonable cleanup.

Minor nit: there are two trailing blank lines at the end of the file which could be reduced to one.

i18n-scripts/common.js (1)

14-25: Unused packageDir parameter and silent error handling.

  1. The packageDir parameter is passed to argFunction but looking at the usage in set-english-defaults.js, updateFile only accepts fileName. This parameter appears unused.

  2. Errors in parseFolder are logged but swallowed, which may hide failures during the i18n build process.

♻️ Suggested improvement
-  parseFolder(directory, argFunction, packageDir) {
+  parseFolder(directory, argFunction) {
     return (async () => {
       try {
         const files = await fs.promises.readdir(directory);
         for (const file of files) {
           const filePath = path.join(directory, file);
-          await argFunction(filePath, packageDir);
+          await argFunction(filePath);
         }
       } catch (e) {
         console.error(`Failed to parseFolder ${directory}:`, e);
+        throw e; // Propagate error to caller
       }
     })();
   },
i18n-scripts/set-english-defaults.js (2)

8-19: Use endsWith() for more precise suffix matching.

Using includes() could match keys like myWithCount_pluralKey incorrectly. Since these are suffixes, endsWith() would be more accurate.

♻️ Suggested improvement
 function determineRule(key) {
-  if (key.includes('WithCount_plural')) {
+  if (key.endsWith('WithCount_plural')) {
     return 0;
   }
-  if (key.includes('WithCount')) {
+  if (key.endsWith('WithCount')) {
     return 1;
   }
-  if (key.includes('_plural')) {
+  if (key.endsWith('_plural')) {
     return 2;
   }
   return 3;
 }

21-27: Node.js require() caches modules, which may cause stale data.

If updateFile is called multiple times on the same file (e.g., during iterative runs), require() will return the cached version from the first load, not the updated file content. Consider using fs.readFileSync with JSON.parse instead.

♻️ Suggested fix
 function updateFile(fileName) {
   let file;
   try {
-    file = require(fileName);
+    const content = fs.readFileSync(fileName, 'utf-8');
+    file = JSON.parse(content);
   } catch (err) {
     throw new Error(`Failed to load ${fileName}: ${err.message}`);
   }
src/utils/units.ts (1)

209-217: Avoid repeated Number(v) coercion calls.

Number(v) is called four times on the same input. Store the coerced value once for clarity and minor efficiency gains.

♻️ Suggested refactor
 export const humanizeCpuCores: HumanizeFunc = (v) => {
-  const value = Number(v) < 1 ? round(Number(v) * 1000) : Number(v);
-  const unit = Number(v) < 1 ? 'm' : '';
+  const num = Number(v);
+  const value = num < 1 ? round(num * 1000) : num;
+  const unit = num < 1 ? 'm' : '';
   return {
     string: `${formatValue(value)}${unit}`,
     unit,
     value,
   };
 };
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bd88e0f and 6107e33.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (29)
  • .eslintrc
  • .eslintrc.yml
  • .gitignore
  • .prettierrc.yml
  • .stylelintrc.yaml
  • Dockerfile
  • README.md
  • deployment/console-plugin-nvidia-gpu/Chart.yaml
  • i18n-scripts/build-i18n.sh
  • i18n-scripts/common.js
  • i18n-scripts/lexers.js
  • i18n-scripts/set-english-defaults.js
  • i18next-parser.config.js
  • package.json
  • src/components/ClusterOverview/GPUProviders.tsx
  • src/components/ClusterOverview/GPUStatus.tsx
  • src/components/GPUDashboard/Cards/InfoCard.tsx
  • src/components/GPUDashboard/Cards/MetricsCard.tsx
  • src/components/GPUDashboard/Cards/WorkloadsCard.css
  • src/components/GPUDashboard/Cards/WorkloadsCard.tsx
  • src/components/GPUDashboard/GPUDashboard.tsx
  • src/components/GPUDashboard/GPUSelector.tsx
  • src/components/NotAvailable.tsx
  • src/components/PodStatus/PodStatus.tsx
  • src/components/shared/OverviewDetailItem.css
  • src/components/shared/OverviewDetailItem.tsx
  • src/utils/units.ts
  • tsconfig.json
  • webpack.config.ts
💤 Files with no reviewable changes (1)
  • .eslintrc
🚧 Files skipped from review as they are similar to previous changes (14)
  • src/components/ClusterOverview/GPUStatus.tsx
  • .stylelintrc.yaml
  • src/components/GPUDashboard/GPUDashboard.tsx
  • src/components/shared/OverviewDetailItem.tsx
  • src/components/ClusterOverview/GPUProviders.tsx
  • i18n-scripts/lexers.js
  • tsconfig.json
  • src/components/GPUDashboard/Cards/WorkloadsCard.css
  • src/components/GPUDashboard/Cards/WorkloadsCard.tsx
  • .gitignore
  • src/components/shared/OverviewDetailItem.css
  • src/components/PodStatus/PodStatus.tsx
  • src/components/GPUDashboard/Cards/InfoCard.tsx
  • README.md
🧰 Additional context used
🧬 Code graph analysis (1)
i18n-scripts/set-english-defaults.js (2)
i18n-scripts/common.js (1)
  • path (2-2)
i18n-scripts/lexers.js (1)
  • keys (9-9)
🔇 Additional comments (26)
src/components/GPUDashboard/GPUSelector.tsx (2)

2-10: LGTM! Imports are correct for PatternFly v6 Select pattern.

The imports align with the PF v6 controlled Select pattern using MenuToggle as the toggle renderer.


38-59: LGTM! Select implementation follows PF v6 patterns correctly.

The controlled Select with MenuToggle toggle renderer, onOpenChange, and onSelect handlers is implemented correctly per PatternFly v6 conventions.

src/components/GPUDashboard/Cards/MetricsCard.tsx (1)

95-95: PatternFly v6 utility class naming is correct.

The migration from v4 to v6 utility classes correctly follows the pf-v6-u-* naming convention for the text alignment utility.

.eslintrc.yml (1)

1-27: LGTM - Well-structured ESLint configuration.

The configuration follows best practices:

  • Correct extends order with prettier last to disable conflicting formatting rules
  • TypeScript parser with JSX support properly configured
  • react/prop-types: off is appropriate since TypeScript handles type checking
  • React version auto-detection avoids manual maintenance

Minor nit: Two trailing blank lines at the end.

i18next-parser.config.js (1)

1-31: LGTM - i18next-parser configuration properly extended.

The configuration adds appropriate lexer mappings for the project's file types. The sort: true option ensures deterministic output ordering, which helps with diff readability in version control. The CustomJSONLexer import from ./i18n-scripts/lexers is correctly defined and exported as a class extending EventEmitter.

Dockerfile (1)

1-13: LGTM - Dockerfile properly upgraded for Node.js 20.

The changes align well with the PR objectives:

  • UBI9 base images (ubi9/nodejs-20 and ubi9/nginx-120) exist and are current
  • Path changes to /usr/src/app follow standard conventions
  • Build artifacts are correctly copied to the nginx runtime stage
webpack.config.ts (7)

8-10: LGTM on production environment detection.

The isProd flag based on NODE_ENV is a standard pattern for webpack configurations. This enables conditional bundling behavior throughout the config.


22-23: Good use of content hashing for cache busting.

Production builds use [hash] and [chunkhash] for long-term caching, while development uses readable names for easier debugging.


43-44: SCSS support added correctly.

The loader chain style-loader → css-loader → sass-loader is the correct order for processing SCSS files.


53-58: ESM interop rule is appropriate.

Setting fullySpecified: false for .mjs and .js files resolves ESM module resolution issues that can occur with some dependencies.


64-64: Security consideration: allowedHosts: 'all' in devServer.

This setting disables host header checking, which is acceptable for local development but ensure this configuration is never used in production-exposed environments. The current setup is development-only, so this is fine.


80-84: LGTM on production optimization settings.

  • devtool: false in production avoids shipping source maps
  • chunkIds: 'deterministic' ensures stable chunk IDs for caching
  • minimize: isProd enables minification only in production

76-78: The patterns array syntax is correct and fully supported by copy-webpack-plugin@^6.4.1. The code follows the official syntax for this version, and the locales directory exists in the repository. No action needed.

Likely an incorrect or invalid review comment.

i18n-scripts/common.js (1)

5-12: LGTM on isDirectory helper.

The try-catch correctly handles the case where lstatSync throws for non-existent paths, returning false as a safe fallback.

i18n-scripts/set-english-defaults.js (3)

29-31: LGTM on input validation.

Good defensive check to ensure the loaded file is a valid object and not an array or primitive.


44-59: LGTM on i18next plural rule transformations.

The transformation logic correctly follows i18next conventions for plural keys (_plural, WithCount, WithCount_plural). The use of the pluralize library for generating plural forms is appropriate.


68-76: LGTM on locale processing flow.

The script correctly filters for the en directory and processes only English locale files. The self-invoking async pattern at startup is appropriate for a CLI script.

package.json (4)

5-8: LGTM on license and engine specification.

Adding the Apache-2.0 license and requiring Node.js >= 20 aligns with the PR objectives for OpenShift 4.19+ compatibility.


84-86: Clarify i18next in both devDependencies and peerDependencies.

i18next appears in both devDependencies (line 42) and peerDependencies (line 85). This is valid if the plugin expects the host application to provide i18next at runtime while using it during development. However, ensure this is intentional.


42-43: No action needed—i18next-parser@^3.11.0 provides the i18next CLI.

The package already includes the i18next binary (bin/cli.js), so the build script dependency is correct.


48-49: No action needed—PatternFly v6 is compatible with React 17.

PatternFly v6 supports both React 17 and React 18 per its documented version policy (supporting two most recent React versions). React ^17.0.1 is compatible with this upgrade.

Likely an incorrect or invalid review comment.

i18n-scripts/build-i18n.sh (1)

1-7: LGTM on i18n extraction script.

Good use of strict shell options (set -exuo pipefail) for fail-fast behavior. The file pattern correctly includes JSON files to work with the CustomJSONLexer. The script already has execute permissions and i18next-parser is available as a dependency in package.json.

src/components/NotAvailable.tsx (1)

8-8: Update CSS class to valid PatternFly v6 utility.

The class .pf-v6-u-color-200 does not exist in PatternFly v6. PatternFly v6 text color utilities use semantic naming (e.g., .pf-v6-u-text-color-subtle, .pf-v6-u-text-color-regular, .pf-v6-u-text-color-disabled).

For secondary/subtle text (the equivalent of v4's .pf-u-color-200), use .pf-v6-u-text-color-subtle.

Likely an incorrect or invalid review comment.

src/utils/units.ts (3)

179-187: LGTM!

Expanding the input type to string | number is appropriate for handling values from APIs that may return numeric data as strings.


189-208: LGTM!

The consistent use of Number(v) for coercion is correct. Invalid inputs like NaN are properly handled by the isFinite checks in the internal humanize and formatValue functions, defaulting to 0.


219-245: LGTM!

The refactored humanizeRatio and humanizePercentage functions correctly normalize inputs and handle edge cases (-0, non-finite values). The logic properly distinguishes between ratio inputs (0-1 range) and percentage inputs (0-100 range). The code compiles without errors in strict TypeScript mode, confirming that the SDK's Humanize type is compatible with these function signatures.

@empovit empovit force-pushed the update-for-ocp-4.19-no-chart-changes branch from 6107e33 to 3fc937c Compare January 13, 2026 15:01
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @src/utils/units.ts:
- Around line 209-218: The humanizeCpuCores function must normalize invalid
numeric input like other helpers: detect when Number(v) is NaN and treat it as 0
before computing value/unit/string. Update humanizeCpuCores to set numValue =
Number(v) and if Number.isNaN(numValue) then set numValue = 0 (or use a guarded
normalized variable), then compute value = numValue < 1 ? round(numValue * 1000)
: numValue, unit = numValue < 1 ? 'm' : '', and return the formatted string
using formatValue so that string and value remain consistent.
🧹 Nitpick comments (2)
i18n-scripts/set-english-defaults.js (2)

8-19: Consider using named constants instead of magic numbers for clarity.

The return values 0, 1, 2, 3 lack semantic meaning. Using named constants would improve readability and maintainability.

♻️ Suggested improvement
+const RULE = {
+  WITH_COUNT_PLURAL: 0,
+  WITH_COUNT: 1,
+  PLURAL: 2,
+  DEFAULT: 3,
+};
+
 function determineRule(key) {
   if (key.includes('WithCount_plural')) {
-    return 0;
+    return RULE.WITH_COUNT_PLURAL;
   }
   if (key.includes('WithCount')) {
-    return 1;
+    return RULE.WITH_COUNT;
   }
   if (key.includes('_plural')) {
-    return 2;
+    return RULE.PLURAL;
   }
-  return 3;
+  return RULE.DEFAULT;
 }

Then update the switch cases to use RULE.WITH_COUNT_PLURAL, etc.


21-27: Using require() for JSON data files can cause caching issues.

Node's require() caches loaded modules, so if this file is processed again (e.g., in watch mode or rerun), you'll get stale data. Use fs.readFileSync with JSON.parse instead for predictable behavior.

♻️ Suggested fix
 function updateFile(fileName) {
   let file;
   try {
-    file = require(fileName);
+    const content = fs.readFileSync(fileName, 'utf8');
+    file = JSON.parse(content);
   } catch (err) {
     throw new Error(`Failed to load ${fileName}: ${err.message}`);
   }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6107e33 and 3fc937c.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (29)
  • .eslintrc
  • .eslintrc.yml
  • .gitignore
  • .prettierrc.yml
  • .stylelintrc.yaml
  • Dockerfile
  • README.md
  • deployment/console-plugin-nvidia-gpu/Chart.yaml
  • i18n-scripts/build-i18n.sh
  • i18n-scripts/common.js
  • i18n-scripts/lexers.js
  • i18n-scripts/set-english-defaults.js
  • i18next-parser.config.js
  • package.json
  • src/components/ClusterOverview/GPUProviders.tsx
  • src/components/ClusterOverview/GPUStatus.tsx
  • src/components/GPUDashboard/Cards/InfoCard.tsx
  • src/components/GPUDashboard/Cards/MetricsCard.tsx
  • src/components/GPUDashboard/Cards/WorkloadsCard.css
  • src/components/GPUDashboard/Cards/WorkloadsCard.tsx
  • src/components/GPUDashboard/GPUDashboard.tsx
  • src/components/GPUDashboard/GPUSelector.tsx
  • src/components/NotAvailable.tsx
  • src/components/PodStatus/PodStatus.tsx
  • src/components/shared/OverviewDetailItem.css
  • src/components/shared/OverviewDetailItem.tsx
  • src/utils/units.ts
  • tsconfig.json
  • webpack.config.ts
💤 Files with no reviewable changes (2)
  • .prettierrc.yml
  • .eslintrc
✅ Files skipped from review due to trivial changes (1)
  • .gitignore
🚧 Files skipped from review as they are similar to previous changes (16)
  • src/components/PodStatus/PodStatus.tsx
  • .stylelintrc.yaml
  • src/components/shared/OverviewDetailItem.css
  • .eslintrc.yml
  • src/components/shared/OverviewDetailItem.tsx
  • src/components/GPUDashboard/GPUSelector.tsx
  • README.md
  • src/components/GPUDashboard/Cards/WorkloadsCard.tsx
  • src/components/ClusterOverview/GPUProviders.tsx
  • i18n-scripts/common.js
  • i18n-scripts/build-i18n.sh
  • i18n-scripts/lexers.js
  • src/components/GPUDashboard/Cards/WorkloadsCard.css
  • Dockerfile
  • src/components/GPUDashboard/GPUDashboard.tsx
  • tsconfig.json
🔇 Additional comments (18)
src/components/GPUDashboard/Cards/MetricsCard.tsx (1)

95-95: LGTM!

The CSS class update from pf-u-text-align-center to pf-v6-u-text-align-center correctly follows the PatternFly v6 utility class naming convention.

src/components/NotAvailable.tsx (1)

8-8: LGTM!

The CSS class update from pf-u-color-200 to pf-v6-u-color-200 correctly follows the PatternFly v6 utility class naming convention, consistent with the broader PF v6 migration in this PR.

deployment/console-plugin-nvidia-gpu/Chart.yaml (1)

4-4: LGTM!

The description update to require OpenShift 4.19+ and the version bump to 0.3.0 are consistent with the PR objectives. The minor version increment is appropriate given the significant dependency upgrades (Console SDK 1.4.0, PatternFly v6, Node.js 20) that necessitate the new OpenShift minimum version.

Also applies to: 15-15

src/components/GPUDashboard/Cards/InfoCard.tsx (1)

3-3: LGTM! Import path updated to use local shared component.

This change correctly migrates from the external @openshift-console/plugin-shared package to the local shared module, which aligns with the Console SDK upgrade. The local OverviewDetailItem component exports the correct API with all required props (title, isLoading, error, children), and the component usage throughout the file is fully compatible.

src/components/ClusterOverview/GPUStatus.tsx (2)

112-116: LGTM!

The icon color assignments correctly reference the okColor string directly. This aligns with the change from the previous JS token object (which required .value) to a CSS variable string.

Also applies to: 147-151


18-19: CSS variable token is correct for PatternFly v6.

The CSS token --pf-t--global--color--status--success--default is a valid PatternFly v6 semantic color token, and the fallback value #3D7317 appropriately provides a green success color. The migration from PF4 JS tokens to PF6 CSS variables is properly implemented.

src/utils/units.ts (4)

179-187: LGTM: Type signature expansion for SDK compatibility.

Accepting string | number aligns with the SDK's Humanize type and provides flexibility for callers passing metric values that may arrive as strings.


189-208: LGTM: Consistent numeric coercion across wrapper functions.

Number(v) correctly handles both string and number inputs, and NaN from invalid strings is handled downstream by humanize() which defaults non-finite values to 0.


220-233: LGTM: Proper NaN and edge-case handling.

The explicit check for non-finite values and -0 normalization ensures consistent behavior. Both string and value fields will reflect the same normalized value.


235-246: LGTM: Consistent with humanizeRatio pattern.

Same robust handling of non-finite values. The division by 100 correctly converts percentage input to ratio for formatPercentage.

i18next-parser.config.js (1)

1-31: LGTM! Well-structured i18next-parser configuration.

The configuration properly sets up lexers for all relevant file types and integrates the custom JSON lexer. The sort: true option ensures consistent, deterministic output which aids in diffing locale files.

webpack.config.ts (2)

61-73: Note: allowedHosts: 'all' allows any host to connect to the dev server.

This is generally acceptable for local development, but ensure this configuration is never used in production-facing environments. The current setup is development-only given it's within devServer, which is appropriate.

Verify this dev server is only used locally and not exposed to external networks in your development workflow.


16-84: LGTM! Clean production-aware webpack configuration.

The configuration properly handles:

  • Production/development mode switching
  • Content hashing for cache busting in production
  • SCSS support with the loader chain
  • ESM module resolution with fullySpecified: false
  • Deterministic chunk IDs for consistent builds
i18n-scripts/set-english-defaults.js (1)

68-81: LGTM! Clean async execution with proper error handling.

The script properly filters for the 'en' locale directory and includes appropriate error handling with a non-zero exit code on failure.

package.json (4)

5-8: LGTM! Clear license and engine requirements.

Good addition of the Apache-2.0 license and Node.js >=20.0.0 engine requirement, aligning with the PR objective to upgrade to Node.js 20.


9-20: LGTM! Well-structured build scripts.

The scripts properly:

  • Set NODE_ENV=production for production builds
  • Use ts-node/register to handle the TypeScript webpack config
  • Chain the i18n extraction and processing steps

83-86: LGTM! Appropriate peer dependency declaration.

Declaring i18next as a peer dependency correctly communicates to the host OpenShift Console that this runtime dependency should be provided by the host application.


21-64: Verify PatternFly and React version compatibility.

The configuration uses:

  • @patternfly/react-charts: ^7.4.2
  • @patternfly/react-core: ^6.2.2
  • react: ^17.0.1

PatternFly v6 typically targets React 18. Verify that these versions work correctly together with React 17.

PatternFly 6 React 17 compatibility requirements

@empovit empovit force-pushed the update-for-ocp-4.19-no-chart-changes branch from 3fc937c to 0bd1259 Compare January 13, 2026 16:06
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @i18n-scripts/set-english-defaults.js:
- Around line 21-27: The updateFile function currently uses require(fileName)
which is cached by Node and yields stale JSON on subsequent runs; replace
require with a synchronous file read and parse (e.g., fs.readFileSync(fileName,
'utf8') followed by JSON.parse) to always reload the file, handle and rethrow
parsing or read errors with a clear message including fileName, and ensure any
downstream code expecting the object continues to receive the parsed object from
updateFile.
🧹 Nitpick comments (4)
src/components/shared/OverviewDetailItem.tsx (1)

14-36: Clean component implementation with proper state handling.

The loading → error → children priority is sensible. One optional simplification for the className on line 33:

♻️ Optional: Simplify className construction
-      <dd className={valueClassName ? `ng-overview-detail-item__value ${valueClassName}` : 'ng-overview-detail-item__value'}>{value}</dd>
+      <dd className={`ng-overview-detail-item__value${valueClassName ? ` ${valueClassName}` : ''}`}>{value}</dd>

Alternatively, consider using a utility like clsx if the project already has it as a dependency for more complex class concatenation scenarios.

src/utils/units.ts (1)

209-221: Consider zero-value display.

When numValue is 0 (or coerced to 0 from non-finite input), the condition numValue < 1 is true, resulting in unit 'm' and displaying "0m" instead of "0". While "0m" (zero millicores) is technically valid in Kubernetes terminology, displaying plain "0" might be cleaner for zero values.

💡 Optional fix to display "0" for zero values
 export const humanizeCpuCores: HumanizeFunc = (v) => {
   let numValue = Number(v);
   if (!isFinite(numValue) || numValue === 0) {
     numValue = 0;
   }
-  const value = numValue < 1 ? round(numValue * 1000) : numValue;
-  const unit = numValue < 1 ? 'm' : '';
+  const value = numValue < 1 && numValue > 0 ? round(numValue * 1000) : numValue;
+  const unit = numValue < 1 && numValue > 0 ? 'm' : '';
   return {
     string: `${formatValue(value)}${unit}`,
     unit,
     value,
   };
 };
i18n-scripts/common.js (1)

14-26: Consider simplifying the async IIFE pattern.

The async IIFE wrapper is unnecessary. An async function already returns a Promise, so you can declare parseFolder as async directly.

♻️ Suggested simplification
-  parseFolder(directory, argFunction, packageDir) {
-    return (async () => {
-      try {
-        const files = await fs.promises.readdir(directory);
-        for (const file of files) {
-          const filePath = path.join(directory, file);
-          await argFunction(filePath, packageDir);
-        }
-      } catch (e) {
-        console.error(`Failed to parseFolder ${directory}:`, e);
-        throw e;
-      }
-    })();
-  },
+  async parseFolder(directory, argFunction, packageDir) {
+    try {
+      const files = await fs.promises.readdir(directory);
+      for (const file of files) {
+        const filePath = path.join(directory, file);
+        await argFunction(filePath, packageDir);
+      }
+    } catch (e) {
+      console.error(`Failed to parseFolder ${directory}:`, e);
+      throw e;
+    }
+  },
i18n-scripts/set-english-defaults.js (1)

8-19: Consider using endsWith for more precise suffix matching.

The current includes() checks could match keys where the suffix appears mid-string (e.g., WithCount_pluralExtra). While unlikely given i18next conventions, using endsWith() would be more precise.

Suggested refinement
 function determineRule(key) {
-  if (key.includes('WithCount_plural')) {
+  if (key.endsWith('WithCount_plural')) {
     return 0;
   }
-  if (key.includes('WithCount')) {
+  if (key.endsWith('WithCount')) {
     return 1;
   }
-  if (key.includes('_plural')) {
+  if (key.endsWith('_plural')) {
     return 2;
   }
   return 3;
 }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3fc937c and 0bd1259.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (30)
  • .dockerignore
  • .eslintrc
  • .eslintrc.yml
  • .gitignore
  • .prettierrc.yml
  • .stylelintrc.yaml
  • Dockerfile
  • README.md
  • deployment/console-plugin-nvidia-gpu/Chart.yaml
  • i18n-scripts/build-i18n.sh
  • i18n-scripts/common.js
  • i18n-scripts/lexers.js
  • i18n-scripts/set-english-defaults.js
  • i18next-parser.config.js
  • package.json
  • src/components/ClusterOverview/GPUProviders.tsx
  • src/components/ClusterOverview/GPUStatus.tsx
  • src/components/GPUDashboard/Cards/InfoCard.tsx
  • src/components/GPUDashboard/Cards/MetricsCard.tsx
  • src/components/GPUDashboard/Cards/WorkloadsCard.css
  • src/components/GPUDashboard/Cards/WorkloadsCard.tsx
  • src/components/GPUDashboard/GPUDashboard.tsx
  • src/components/GPUDashboard/GPUSelector.tsx
  • src/components/NotAvailable.tsx
  • src/components/PodStatus/PodStatus.tsx
  • src/components/shared/OverviewDetailItem.css
  • src/components/shared/OverviewDetailItem.tsx
  • src/utils/units.ts
  • tsconfig.json
  • webpack.config.ts
💤 Files with no reviewable changes (2)
  • .eslintrc
  • .prettierrc.yml
🚧 Files skipped from review as they are similar to previous changes (16)
  • src/components/ClusterOverview/GPUStatus.tsx
  • src/components/GPUDashboard/GPUDashboard.tsx
  • src/components/GPUDashboard/Cards/WorkloadsCard.tsx
  • tsconfig.json
  • src/components/NotAvailable.tsx
  • src/components/GPUDashboard/Cards/InfoCard.tsx
  • i18n-scripts/lexers.js
  • src/components/GPUDashboard/GPUSelector.tsx
  • .stylelintrc.yaml
  • src/components/GPUDashboard/Cards/WorkloadsCard.css
  • i18next-parser.config.js
  • src/components/ClusterOverview/GPUProviders.tsx
  • .gitignore
  • src/components/GPUDashboard/Cards/MetricsCard.tsx
  • deployment/console-plugin-nvidia-gpu/Chart.yaml
  • README.md
🧰 Additional context used
🧬 Code graph analysis (1)
i18n-scripts/common.js (2)
i18n-scripts/set-english-defaults.js (3)
  • fs (1-1)
  • path (2-2)
  • file (22-22)
i18next-parser.config.js (1)
  • require (2-2)
🔇 Additional comments (23)
src/components/shared/OverviewDetailItem.css (1)

1-18: LGTM!

The CSS follows the project's ng- prefix convention, uses PatternFly v6 CSS variables consistently, and employs good defensive styling with word-break and overflow-wrap for handling long content in values.

src/components/shared/OverviewDetailItem.tsx (1)

1-12: LGTM!

Clean imports and well-defined props interface. Using semantic dl/dt/dd elements is appropriate for a definition list-style component and benefits accessibility.

src/utils/units.ts (4)

179-187: LGTM!

The type signature change to accept string | number is backward-compatible and aligns with common patterns where metric values may arrive as strings from APIs or form inputs.


189-208: LGTM!

Consistent application of Number(v) coercion across all wrapper functions. The downstream humanize function already handles non-finite values gracefully.


223-236: LGTM!

The type change to Humanize (from SDK) aligns with the PR objective to upgrade to Console SDK 1.4.0. The numeric coercion and formatting logic is correct—ratio values are properly converted to percentage display via formatPercentage.


238-249: LGTM!

Consistent with the pattern in humanizeRatio. The division by 100 before calling formatPercentage correctly handles the input being a percentage value (e.g., 50) rather than a ratio (e.g., 0.5), leveraging Intl.NumberFormat's percent style.

src/components/PodStatus/PodStatus.tsx (2)

3-3: LGTM!

The import correctly uses Content from PatternFly v6, replacing the deprecated TextContent, Text, and TextVariants from v4.


116-149: PatternFly v6 migration looks correct.

The restructuring properly migrates from PatternFly v4 to v6:

  • Fragment wrapper replaces TextContent appropriately
  • <Content component="p"> is the correct v6 replacement for <Text>
  • Link paths consistently use pod.metadata.name
.dockerignore (1)

1-4: LGTM!

Standard .dockerignore patterns that correctly exclude build artifacts, dependencies, and version control from the Docker build context. This keeps the context lean and aligns with the Dockerfile's yarn install --frozen-lockfile && yarn build workflow.

i18n-scripts/build-i18n.sh (1)

1-7: LGTM!

The script correctly uses strict shell options (set -exuo pipefail) for fail-fast behavior, and the escaped \$LOCALE and \$NAMESPACE are proper i18next-parser template placeholders.

.eslintrc.yml (1)

1-25: LGTM!

Well-structured ESLint configuration for TypeScript and React with Prettier integration. Disabling react/prop-types is appropriate since TypeScript handles type checking, and auto-detecting the React version avoids version mismatches.

i18n-scripts/common.js (2)

5-12: LGTM!

The isDirectory function correctly uses lstatSync with appropriate error handling, returning false for non-existent paths.


28-35: LGTM!

The deleteFile function has proper error handling with logging and re-throwing to preserve the error for callers.

Dockerfile (1)

1-13: LGTM. The Dockerfile correctly upgrades to Node.js 20 on UBI 9, uses a conditional yarn installation pattern, and maintains proper security by running as non-root (USER 1001) in the final stage. Both base images are available and the build stage path changes are consistent throughout.

webpack.config.ts (4)

61-73: allowedHosts: 'all' exposes the dev server to potential DNS rebinding attacks.

When the dev server is accessible beyond localhost (e.g., via container port forwarding or network exposure), allowedHosts: 'all' disables host header checks, which could allow malicious sites to interact with your dev server.

If the dev server is only used locally, this is acceptable. If it may be exposed on a network (common in container/OpenShift development workflows), consider restricting to specific hosts or using allowedHosts: 'auto'.

Is this dev server ever exposed beyond localhost in your development workflow (e.g., via OpenShift routes or port forwarding)?


7-8: CommonJS require for copy-webpack-plugin is appropriate for v6.x.

The copy-webpack-plugin v6.x exports a CommonJS module, making the require syntax with the eslint-disable comment the correct approach here.

Also applies to: 76-78


16-24: Production/development build configuration looks solid.

The environment-aware configuration properly differentiates between production (hashed filenames, minification, deterministic chunk IDs, no source maps) and development (readable names, source maps, named chunks). This aligns with webpack best practices.

Also applies to: 80-84


53-58: ESM compatibility rule is correctly configured.

Setting fullySpecified: false for .mjs and .js files resolves ESM import issues where packages don't include file extensions. This is commonly needed for modern ESM-aware packages.

i18n-scripts/set-english-defaults.js (1)

74-81: Entry point has proper error handling.

The async IIFE correctly catches errors, logs them, and exits with a non-zero code to fail the build pipeline on errors.

package.json (4)

5-8: License and engine specifications are good additions.

Explicitly declaring the Apache-2.0 license and Node.js 20+ requirement improves project clarity and prevents runtime environment mismatches.


9-19: Build scripts are cleaner with the unified webpack approach.

The refactored scripts properly set NODE_ENV=production for production builds and use a consistent webpack invocation pattern via ts-node for TypeScript config support.


42-42: Remove version compatibility warning—react-i18next 11.x is compatible with i18next 23.x.

react-i18next 11.x officially supports i18next >= 10.0.0, and the project's i18next ^23.11.5 satisfies this requirement. The peerDependencies section explicitly declares this pairing, confirming it is an intentional and compatible configuration.

Likely an incorrect or invalid review comment.


24-27: > Likely an incorrect or invalid review comment.

@empovit empovit force-pushed the update-for-ocp-4.19-no-chart-changes branch from 0bd1259 to c1d8515 Compare January 13, 2026 16:20
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @src/components/ClusterOverview/GPUStatus.tsx:
- Around line 18-19: The fallback hex for the success token is wrong; update the
okColor constant so its CSS var fallback matches PatternFly v6 by replacing the
hardcoded fallback #3D7317 with #3e8635 (i.e., change the value of the okColor
variable to use 'var(--pf-t--global--color--status--success--default,
#3e8635)').

In @src/utils/units.ts:
- Around line 209-221: In humanizeCpuCores, when numValue is 0 the current logic
treats it as <1 and sets unit='m' producing "0m"; change the conditional so zero
is handled explicitly: if numValue === 0 set value = 0 and unit = '' (and string
= `${formatValue(0)}`) before the numValue < 1 branch, ensuring invalid inputs
coerced to 0 also produce "0" not "0m"; update the return to use these adjusted
value/unit variables in humanizeCpuCores.
🧹 Nitpick comments (2)
src/utils/units.ts (1)

251-261: Consider aligning humanizeGPU and humanizeDegrees with the new coercion pattern.

These functions use Number.parseInt() for the numeric value but template literals for the string, which could produce inconsistent results (e.g., input "3.5" → string "3.5", value 3).

Since these weren't modified in this PR, this is optional follow-up work to maintain consistency with the other humanize functions.

package.json (1)

35-35: Consider upgrading copy-webpack-plugin in a follow-up.

Version ^6.4.1 is quite dated (current stable is 12.x). While functional, newer versions offer better webpack 5 integration and security patches. This could be addressed in a separate PR to keep this upgrade focused.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0bd1259 and c1d8515.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (30)
  • .dockerignore
  • .eslintrc
  • .eslintrc.yml
  • .gitignore
  • .prettierrc.yml
  • .stylelintrc.yaml
  • Dockerfile
  • README.md
  • deployment/console-plugin-nvidia-gpu/Chart.yaml
  • i18n-scripts/build-i18n.sh
  • i18n-scripts/common.js
  • i18n-scripts/lexers.js
  • i18n-scripts/set-english-defaults.js
  • i18next-parser.config.js
  • package.json
  • src/components/ClusterOverview/GPUProviders.tsx
  • src/components/ClusterOverview/GPUStatus.tsx
  • src/components/GPUDashboard/Cards/InfoCard.tsx
  • src/components/GPUDashboard/Cards/MetricsCard.tsx
  • src/components/GPUDashboard/Cards/WorkloadsCard.css
  • src/components/GPUDashboard/Cards/WorkloadsCard.tsx
  • src/components/GPUDashboard/GPUDashboard.tsx
  • src/components/GPUDashboard/GPUSelector.tsx
  • src/components/NotAvailable.tsx
  • src/components/PodStatus/PodStatus.tsx
  • src/components/shared/OverviewDetailItem.css
  • src/components/shared/OverviewDetailItem.tsx
  • src/utils/units.ts
  • tsconfig.json
  • webpack.config.ts
💤 Files with no reviewable changes (2)
  • .prettierrc.yml
  • .eslintrc
🚧 Files skipped from review as they are similar to previous changes (19)
  • src/components/GPUDashboard/Cards/MetricsCard.tsx
  • tsconfig.json
  • src/components/GPUDashboard/GPUDashboard.tsx
  • .dockerignore
  • i18n-scripts/lexers.js
  • src/components/shared/OverviewDetailItem.tsx
  • README.md
  • i18n-scripts/common.js
  • src/components/GPUDashboard/Cards/WorkloadsCard.css
  • i18n-scripts/set-english-defaults.js
  • .gitignore
  • src/components/GPUDashboard/GPUSelector.tsx
  • i18n-scripts/build-i18n.sh
  • src/components/shared/OverviewDetailItem.css
  • src/components/GPUDashboard/Cards/WorkloadsCard.tsx
  • .stylelintrc.yaml
  • src/components/NotAvailable.tsx
  • deployment/console-plugin-nvidia-gpu/Chart.yaml
  • Dockerfile
🔇 Additional comments (14)
src/components/ClusterOverview/GPUProviders.tsx (1)

2-2: LGTM!

The import path correctly points to the new local OverviewDetailItem component at ../shared/OverviewDetailItem, which aligns with the PR's approach of creating shared UI components to support the Console SDK and PatternFly upgrades.

src/components/GPUDashboard/Cards/InfoCard.tsx (1)

3-3: LGTM - Import migrated to local shared component.

The import path change from @openshift-console/plugin-shared to the local ../../shared/OverviewDetailItem aligns with the Console SDK 1.4.0 upgrade. The component usage throughout the file remains consistent with the expected props interface (title, isLoading, error, children).

src/components/PodStatus/PodStatus.tsx (2)

3-3: LGTM - PatternFly v6 import migration.

The Content component correctly replaces the deprecated TextContent/Text imports for PatternFly v6.


116-149: LGTM - PatternFly v6 Content component migration and URL fixes.

The migration from TextContent/Text to Content component="p" follows PatternFly v6 patterns correctly. The URL paths at lines 126, 130, and 140 correctly reference pod.metadata.name.

src/utils/units.ts (4)

179-187: LGTM!

The type change to accept string | number aligns with common API response patterns where numeric values may arrive as strings. This improves flexibility while maintaining type safety.


189-208: LGTM!

Consistent use of Number(v) across all wrapper functions. Invalid inputs resulting in NaN are safely handled by the downstream humanize() function's !isFinite(value) check.


223-236: LGTM!

The ratio-to-percentage conversion logic is correct. The Humanize type from the SDK provides better type alignment with the OpenShift Console API.


238-249: LGTM!

Correct percentage handling with proper division by 100 before formatting with Intl.NumberFormat's percent style.

.eslintrc.yml (1)

1-25: LGTM! Well-structured ESLint configuration.

The configuration properly chains extends (with prettier last to avoid conflicts), enables JSX support, and disables react/prop-types since TypeScript handles type checking. Auto-detecting the React version is a good practice.

i18next-parser.config.js (1)

1-30: LGTM! Comprehensive lexer configuration for i18n extraction.

The configuration properly maps file extensions to appropriate lexers. Note that CustomJSONLexer (line 27) is correctly passed as a class reference rather than a string, since it's a custom implementation imported from the local i18n-scripts/lexers module.

webpack.config.ts (2)

61-73: Verify allowedHosts: 'all' is acceptable for your environment.

Setting allowedHosts: 'all' disables host checking entirely, which is convenient for development but could expose the dev server to DNS rebinding attacks if run on a network accessible to untrusted clients. This is generally acceptable for local development only.


74-84: LGTM! Clean production-aware configuration.

The webpack config properly differentiates between production and development modes with appropriate optimizations:

  • Hash-based filenames for cache busting in production
  • Source maps only in development
  • Deterministic chunk IDs for production, named for debugging in development
package.json (1)

5-19: LGTM! Scripts and engine constraints are well-defined.

The engines field properly enforces Node.js 20+ as stated in PR objectives. The refactored scripts cleanly separate concerns:

  • webpack script abstracts the ts-node registration
  • build/build-dev use the shared webpack script with appropriate NODE_ENV
  • type-check enables standalone TypeScript validation
src/components/ClusterOverview/GPUStatus.tsx (1)

115-116: LGTM!

The okColor CSS variable is correctly applied to the CheckCircleIcon for both temperature and operator healthy states. The color prop accepts CSS variable strings, so this will resolve appropriately at runtime.

Also applies to: 150-151

@empovit empovit force-pushed the update-for-ocp-4.19-no-chart-changes branch from c1d8515 to 51c82b8 Compare January 13, 2026 16:50
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In @package.json:
- Line 61: Update the webpack dependency in package.json from "5.75.0" to a
secure, patched version (preferably "5.94.0" or at minimum "5.76.0") and change
the version spec to a caret range (e.g., ^5.94.0) so future security patches are
picked up; after updating the "webpack" entry, run your package manager
(npm/yarn) to update lockfile (package-lock.json or yarn.lock) and run the
test/build pipeline to verify there are no breakages.
- Around line 42-43: package.json lists "i18next" at ^23.11.5 but
"i18next-parser" is pinned to the old 3.x series; update the dependency entry
for "i18next-parser" to a 9.3.0+ spec (for example "^9.3.0"), run your package
manager install (npm/yarn/pnpm) to refresh lockfiles, and run the i18next-parser
build/extraction to catch any config/api breaking changes — if extraction fails
adjust the i18next-parser configuration (e.g., keysSeparator/namespace handling)
to match the new parser API used by the updated "i18next-parser".

In @src/components/shared/OverviewDetailItem.tsx:
- Around line 24-25: The span rendering error text in OverviewDetailItem.tsx
currently uses a non-existent PatternFly v6 utility class; replace that by
adding a CSS rule in OverviewDetailItem.css that sets color to the PatternFly
danger token (var(--pf-t--global--text--color--status--danger--default)) and
then change the span in OverviewDetailItem.tsx to use a descriptive className
(e.g., ng-overview-detail-item__error) so the error text uses the design token
instead of the invalid utility class.

In @src/utils/units.ts:
- Around line 209-221: humanizeCpuCores currently treats numValue < 1 as
millicores so a zero input yields "0m"; add an explicit check at the start of
humanizeCpuCores for numValue === 0 and return { string: `${formatValue(0)}`,
unit: '', value: 0 } (or equivalent) to ensure zero displays as "0" (use the
existing symbols numValue, value, unit, round, formatValue and preserve the rest
of the logic for values <1 and >=1).
🧹 Nitpick comments (2)
webpack.config.ts (1)

61-73: Security consideration for dev server configuration.

allowedHosts: 'all' disables host header validation, which can expose the dev server to DNS rebinding attacks. This is acceptable for local development but ensure this configuration is never used in any exposed environment.

Consider documenting this as a dev-only setting or restricting to localhost in comments:

  devServer: {
    static: './dist',
    port: 9001,
-   allowedHosts: 'all',
+   // WARNING: 'all' disables host checking - for local development only
+   allowedHosts: 'all',
    headers: {
src/components/GPUDashboard/GPUSelector.tsx (1)

47-56: Consider displaying a human-readable GPU identifier instead of the raw UUID.

The toggle displays selectedGPU?.uuid which shows a raw UUID to users. For better UX, consider showing the model name or a combination of model and a short identifier.

💡 Suggested improvement
         <MenuToggle
           ref={toggleRef}
           onClick={() => setOpen(!isOpen)}
           isExpanded={isOpen}
           isDisabled={isDisabled}
         >
-          {selectedGPU?.uuid || t('Select GPU')}
+          {selectedGPU?.modelName || selectedGPU?.uuid || t('Select GPU')}
         </MenuToggle>
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c1d8515 and 51c82b8.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (30)
  • .dockerignore
  • .eslintrc
  • .eslintrc.yml
  • .gitignore
  • .prettierrc.yml
  • .stylelintrc.yaml
  • Dockerfile
  • README.md
  • deployment/console-plugin-nvidia-gpu/Chart.yaml
  • i18n-scripts/build-i18n.sh
  • i18n-scripts/common.js
  • i18n-scripts/lexers.js
  • i18n-scripts/set-english-defaults.js
  • i18next-parser.config.js
  • package.json
  • src/components/ClusterOverview/GPUProviders.tsx
  • src/components/ClusterOverview/GPUStatus.tsx
  • src/components/GPUDashboard/Cards/InfoCard.tsx
  • src/components/GPUDashboard/Cards/MetricsCard.tsx
  • src/components/GPUDashboard/Cards/WorkloadsCard.css
  • src/components/GPUDashboard/Cards/WorkloadsCard.tsx
  • src/components/GPUDashboard/GPUDashboard.tsx
  • src/components/GPUDashboard/GPUSelector.tsx
  • src/components/NotAvailable.tsx
  • src/components/PodStatus/PodStatus.tsx
  • src/components/shared/OverviewDetailItem.css
  • src/components/shared/OverviewDetailItem.tsx
  • src/utils/units.ts
  • tsconfig.json
  • webpack.config.ts
💤 Files with no reviewable changes (2)
  • .prettierrc.yml
  • .eslintrc
🚧 Files skipped from review as they are similar to previous changes (12)
  • src/components/NotAvailable.tsx
  • src/components/GPUDashboard/Cards/MetricsCard.tsx
  • src/components/PodStatus/PodStatus.tsx
  • i18n-scripts/common.js
  • i18n-scripts/set-english-defaults.js
  • src/components/ClusterOverview/GPUProviders.tsx
  • .gitignore
  • src/components/GPUDashboard/Cards/WorkloadsCard.css
  • tsconfig.json
  • src/components/ClusterOverview/GPUStatus.tsx
  • README.md
  • deployment/console-plugin-nvidia-gpu/Chart.yaml
🧰 Additional context used
🧬 Code graph analysis (2)
i18n-scripts/lexers.js (2)
i18next-parser.config.js (1)
  • require (2-2)
i18n-scripts/set-english-defaults.js (1)
  • keys (35-35)
src/components/GPUDashboard/GPUDashboard.tsx (1)
src/components/GPUDashboard/Cards/GPUDashboardGraphs.tsx (1)
  • GPUDashboardGraphs (48-146)
🔇 Additional comments (21)
src/utils/units.ts (4)

179-187: LGTM!

The type signature update to accept string | number is appropriate for handling inputs that may arrive as strings (e.g., from JSON or query parameters), with internal coercion ensuring numeric processing.


189-208: LGTM!

Consistent application of Number(v) coercion across all wrapper functions ensures robust handling of mixed input types while maintaining the existing numeric processing logic.


238-249: LGTM!

The implementation correctly handles percentage values with proper coercion and formatting. The division by 100 before formatPercentage is necessary since Intl.NumberFormat with style: 'percent' multiplies by 100 internally.


223-236: LGTM!

The logic correctly handles ratio-to-percentage conversion with appropriate numeric coercion and edge case handling. The function properly implements the SDK's Humanize type signature—it accepts a value parameter and returns the expected HumanizeResult object with string, unit, and value properties.

.dockerignore (1)

1-4: LGTM!

Standard ignore patterns that correctly exclude build artifacts, dependencies, version control, and logs from the Docker build context. This reduces context size and ensures clean builds.

.eslintrc.yml (1)

1-27: LGTM!

Well-structured ESLint configuration for TypeScript + React with Prettier integration. Disabling react/prop-types is appropriate since TypeScript handles type checking. The react.version: detect setting is a good practice.

Minor: Two trailing blank lines at end of file (lines 26-27) could be reduced to one.

Dockerfile (1)

1-13: LGTM!

Good upgrade to Node.js 20 on UBI9, aligning with the PR objectives. The build layout is clean with consistent paths, and the final image correctly runs as non-root user (1001). The multi-stage build keeps the final image lean by only copying the dist artifacts.

.stylelintrc.yaml (1)

1-28: LGTM!

Well-documented Stylelint configuration that enforces PatternFly best practices. Key decisions are sound:

  • Disallowing named/hex colors and rgb() ensures dark mode compatibility via PF global variables
  • Protecting .pf- and .co- prefixes prevents conflicts with PatternFly and OpenShift console
  • Allowing BEM-style classnames while restricting bare element selectors is appropriate for plugin development
webpack.config.ts (2)

16-24: LGTM!

Clean production/development mode handling with appropriate settings:

  • Hash-based filenames in production for cache busting
  • Source maps disabled in production, enabled in development
  • Deterministic chunk IDs in production for long-term caching
  • Minimization correctly gated behind isProd

Also applies to: 80-85


74-79: The locales directory exists and contains the expected i18n files. No action needed.

src/components/GPUDashboard/Cards/InfoCard.tsx (1)

3-3: LGTM! Good migration to local component.

Moving OverviewDetailItem from the external @openshift-console/plugin-shared to a local shared module is a sensible approach for the Console SDK upgrade. This provides better control over the component's behavior and styling while reducing external dependencies.

src/components/GPUDashboard/Cards/WorkloadsCard.tsx (2)

48-55: LGTM! Consistent CSS class naming convention.

The rename from co-break-word to ng-break-word correctly follows the template CSS naming conventions (ng- prefix) mentioned in the PR objectives. The change is applied consistently across all table columns.


42-45: The EmptyState API usage is correct for PatternFly v6. The icon prop properly accepts the CubesIcon component, and EmptyStateBody as a child element is valid. This is the correct and only way to implement empty states in PatternFly v6, as EmptyStateIcon is now internal to the component.

src/components/GPUDashboard/GPUDashboard.tsx (1)

26-55: LGTM! Clean layout restructuring.

The refactored layout using Stack, Split, and Grid follows PatternFly v6 best practices:

  • PageSection directly without a Page wrapper simplifies the component hierarchy
  • Split with isFilled correctly allocates space between title and selector
  • Grid with span={6} provides equal width for MIGCard and InfoCard
  • Stack with hasGutter ensures consistent vertical spacing

The structure is readable and maintainable.

i18n-scripts/lexers.js (1)

7-29: LGTM! Well-implemented custom lexer.

The CustomJSONLexer correctly:

  • Extends EventEmitter (required by i18next-parser's lexer interface)
  • Uses comment-json to handle JSON with comments
  • Extracts keys matching the %key% pattern via the reviver callback
  • Provides proper error handling with filename context

The implementation is clean and follows the expected lexer contract.

i18n-scripts/build-i18n.sh (1)

1-7: Script correctly uses strict mode and glob pattern handling.

The script properly uses set -exuo pipefail for fail-fast behavior. The FILE_PATTERN in single quotes is correctly passed to i18next-parser as a literal string, allowing the tool's internal glob library to process the pattern—this is the right approach rather than shell expansion. The output file locales/en/plugin__console-plugin-nvidia-gpu.json confirms the script executes successfully with the configured pattern.

src/components/shared/OverviewDetailItem.css (1)

1-18: LGTM! Well-structured CSS using PatternFly v6 design tokens.

The CSS follows the ng- prefix convention as intended, uses appropriate PF6 design tokens (--pf-t--global--*), and the word-break/overflow-wrap on the value class properly handles long text overflow.

i18next-parser.config.js (1)

15-30: LGTM! Lexers configuration is properly structured.

The configuration correctly distinguishes between built-in lexers (referenced by string names like 'JavascriptLexer') and the custom CustomJSONLexer (passed as a class reference). This is the expected pattern for i18next-parser custom lexer integration.

src/components/GPUDashboard/GPUSelector.tsx (1)

39-59: PatternFly 6 Select implementation looks correct.

The migration to PF6's controlled Select with MenuToggle pattern is properly implemented. State management via isOpen/onOpenChange and selection handling via onSelect follow the expected PF6 patterns.

src/components/shared/OverviewDetailItem.tsx (1)

14-36: Well-structured reusable component.

The component properly handles loading, error, and content states with appropriate semantic HTML (dl/dt/dd). The props interface is clean and the conditional rendering logic is straightforward.

package.json (1)

84-86: Appropriate use of peerDependencies for i18next.

Adding i18next as a peer dependency is correct for OpenShift Console plugins, as the host console provides the i18next instance. This ensures version compatibility without bundling a duplicate.

@empovit empovit force-pushed the update-for-ocp-4.19-no-chart-changes branch 2 times, most recently from f2d0c5d to 58fbe0d Compare January 13, 2026 17:31
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In @locales/en/plugin__console-plugin-nvidia-gpu.json:
- Around line 2-5: The pluralization entries mistakenly include the i18next key
suffixes in their values (e.g., "{{count}} GPU pod_one"); update the values for
the keys "{{count}} GPU pod_one", "{{count}} GPU pod_other", "{{count}} Graphics
Cards_one", and "{{count}} Graphics Cards_other" to remove the "_one"/"_other"
suffixes so they contain only the proper display text with interpolation (for
example "{{count}} GPU pod" for the _one form and "{{count}} GPU pods" for the
_other form, and similarly "Graphics Card"/"Graphics Cards").

In @package.json:
- Line 61: The package.json currently pins "webpack": "5.75.0", which is
vulnerable; update the webpack dependency to at least "5.76.0" (or "5.94.0" to
also address the DOM-clobbering XSS) by changing the "webpack" version string in
package.json and then run your package manager install and regenerate lockfile
(npm install or yarn install / pnpm install) to update the lockfile; ensure any
CI or build references that pin webpack are updated to the new version as well.
- Line 35: The package.json entry for copy-webpack-plugin is pinned to "^6.4.1";
either update that dependency to a current compatible release (e.g., the latest
v13.x if your webpack setup supports it) by changing the version string for
"copy-webpack-plugin", or if v6 is intentionally required for compatibility, add
a comment in package.json (or in the repo README/DEPENDENCIES note) explaining
why v6 is pinned and any compatibility constraints; ensure any upgrade is tested
against webpack config and CI to catch breaking changes in copy-webpack-plugin's
major versions.

In @README.md:
- Line 24: Update the compatibility table in the README to add a row documenting
support for OpenShift 4.19+ by the new plugin/chart version 0.3.0: insert an
entry mapping "OpenShift (OCP) 4.19+" (or "4.19+") to the plugin/chart version
"0.3.0" and ensure the table formatting matches existing rows (same columns and
alignment) so the new entry appears next to the other OCP ranges.
🧹 Nitpick comments (6)
src/components/ClusterOverview/GPUProviders.tsx (1)

11-38: Consider simplifying the nested conditionals.

The deeply nested if/else structure is harder to read and maintain. A lookup-based approach would be cleaner.

♻️ Optional: Simplify with a lookup pattern
-  let result = t('No GPUs detected');
-  if (providers.nvidia) {
-    if (providers.amd) {
-      if (providers.other) {
-        result = t('NVIDIA, AMD and others');
-      } else {
-        result = t('NVIDIA and AMD');
-      }
-    } else {
-      if (providers.other) {
-        result = t('NVIDIA and others');
-      } else {
-        result = t('NVIDIA');
-      }
-    }
-  } else {
-    if (providers.amd) {
-      if (providers.other) {
-        result = t('AMD and others');
-      } else {
-        result = t('AMD');
-      }
-    } else {
-      if (providers.other) {
-        result = t('others');
-      }
-      // else no GPU
-    }
-  }
+  const key = `${providers.nvidia ? 'n' : ''}${providers.amd ? 'a' : ''}${providers.other ? 'o' : ''}`;
+  const labels: Record<string, string> = {
+    nao: t('NVIDIA, AMD and others'),
+    na: t('NVIDIA and AMD'),
+    no: t('NVIDIA and others'),
+    n: t('NVIDIA'),
+    ao: t('AMD and others'),
+    a: t('AMD'),
+    o: t('others'),
+  };
+  const result = labels[key] ?? t('No GPUs detected');
src/components/shared/OverviewDetailItem.tsx (2)

22-23: Consider adding accessibility attributes to the loading spinner.

Screen readers benefit from knowing content is loading. PatternFly's Spinner supports an aria-label prop.

♻️ Optional: Add aria-label for accessibility
   if (isLoading) {
-    value = <Spinner size="md" />;
+    value = <Spinner size="md" aria-label="Loading" />;
   } else if (error) {

33-33: Optional: Simplify className concatenation.

Using template literals consistently improves readability.

♻️ Optional: Use template literal
-      <dd className={valueClassName ? `ng-overview-detail-item__value ${valueClassName}` : 'ng-overview-detail-item__value'}>{value}</dd>
+      <dd className={`ng-overview-detail-item__value${valueClassName ? ` ${valueClassName}` : ''}`}>{value}</dd>
src/components/PodStatus/PodStatus.tsx (1)

64-67: Pre-existing bug: potential null reference in unchanged code.

This is not introduced by this PR, but line 65 accesses terminated.signal and terminated.exitCode when waiting is truthy (and waiting.reason is falsy). However, terminated could be undefined in this branch, causing a runtime error.

Consider addressing this in a follow-up:

       } else if (waiting && !waiting.reason) {
-        phase = terminated.signal
-          ? `Signal:${terminated.signal}`
-          : `ExitCode:${terminated.exitCode}`;
+        phase = waiting.message || 'Waiting';
       } else if (running && ready) {
src/utils/units.ts (1)

209-221: LGTM, minor edge case to consider.

The numeric coercion and normalization logic is correct. One minor observation: when the input is 0, the condition numValue < 1 evaluates to true, resulting in "0m" (millicores) rather than "0". This is technically accurate but may differ from user expectations for zero values.

If displaying "0" for zero cores is preferred:

🔧 Optional adjustment
 export const humanizeCpuCores: HumanizeFunc = (v) => {
   let numValue = Number(v);
   if (!isFinite(numValue) || numValue === 0) {
     numValue = 0;
   }
-  const value = numValue < 1 ? round(numValue * 1000) : numValue;
-  const unit = numValue < 1 ? 'm' : '';
+  const value = numValue < 1 && numValue !== 0 ? round(numValue * 1000) : numValue;
+  const unit = numValue < 1 && numValue !== 0 ? 'm' : '';
   return {
     string: `${formatValue(value)}${unit}`,
     unit,
     value,
   };
 };
webpack.config.ts (1)

61-73: Consider restricting allowedHosts for development security.

While allowedHosts: 'all' is common for local development, it allows any host to connect to the dev server. This is acceptable for isolated development but could be a concern if the dev server is exposed on a network.

If the dev server is only used locally, consider using allowedHosts: ['localhost', '127.0.0.1'] instead. Otherwise, ensure documentation warns developers not to expose the dev server externally.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 51c82b8 and 58fbe0d.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (33)
  • .dockerignore
  • .eslintrc
  • .eslintrc.yml
  • .gitignore
  • .prettierrc.yml
  • .stylelintrc.yaml
  • Dockerfile
  • README.md
  • deployment/console-plugin-nvidia-gpu/Chart.yaml
  • i18n-scripts/build-i18n.sh
  • i18n-scripts/common.js
  • i18n-scripts/lexers.js
  • i18n-scripts/set-english-defaults.js
  • i18next-parser.config.js
  • locales/en/plugin__console-plugin-nvidia-gpu.json
  • package.json
  • src/components/ClusterOverview/GPUProviders.tsx
  • src/components/ClusterOverview/GPUStatus.tsx
  • src/components/GPUDashboard/Cards/InfoCard.tsx
  • src/components/GPUDashboard/Cards/MetricsCard.css
  • src/components/GPUDashboard/Cards/MetricsCard.tsx
  • src/components/GPUDashboard/Cards/WorkloadsCard.css
  • src/components/GPUDashboard/Cards/WorkloadsCard.tsx
  • src/components/GPUDashboard/GPUDashboard.tsx
  • src/components/GPUDashboard/GPUSelector.tsx
  • src/components/NotAvailable.css
  • src/components/NotAvailable.tsx
  • src/components/PodStatus/PodStatus.tsx
  • src/components/shared/OverviewDetailItem.css
  • src/components/shared/OverviewDetailItem.tsx
  • src/utils/units.ts
  • tsconfig.json
  • webpack.config.ts
💤 Files with no reviewable changes (2)
  • .prettierrc.yml
  • .eslintrc
🚧 Files skipped from review as they are similar to previous changes (16)
  • src/components/shared/OverviewDetailItem.css
  • src/components/GPUDashboard/Cards/WorkloadsCard.tsx
  • tsconfig.json
  • .stylelintrc.yaml
  • i18n-scripts/build-i18n.sh
  • i18next-parser.config.js
  • .gitignore
  • i18n-scripts/set-english-defaults.js
  • .eslintrc.yml
  • src/components/GPUDashboard/Cards/MetricsCard.tsx
  • i18n-scripts/common.js
  • i18n-scripts/lexers.js
  • src/components/GPUDashboard/GPUSelector.tsx
  • deployment/console-plugin-nvidia-gpu/Chart.yaml
  • src/components/NotAvailable.tsx
  • src/components/GPUDashboard/Cards/InfoCard.tsx
🔇 Additional comments (25)
src/components/ClusterOverview/GPUProviders.tsx (1)

2-2: LGTM on the import change.

The migration from @openshift-console/plugin-shared to the local shared component aligns with the SDK upgrade objectives.

src/components/GPUDashboard/Cards/MetricsCard.css (1)

9-11: LGTM!

The new .ng-metrics-card__title class follows the ng- prefix naming convention and provides centered alignment for card titles.

src/components/GPUDashboard/Cards/WorkloadsCard.css (1)

6-9: LGTM!

The .ng-break-word utility class provides robust text wrapping. Using both word-break: break-word and overflow-wrap: anywhere ensures good fallback behavior across browsers.

src/components/shared/OverviewDetailItem.tsx (1)

14-36: LGTM on the new shared component.

Clean implementation with proper TypeScript types and state handling for loading, error, and content states.

src/components/GPUDashboard/GPUDashboard.tsx (1)

26-55: LGTM on the simplified layout structure.

The restructured layout using Stack, Split, and Grid is clean and follows PatternFly v6 patterns well. The 50/50 grid split for MIGCard and InfoCard is appropriate, and the hasGutter props ensure consistent spacing.

src/components/NotAvailable.css (1)

1-3: Verify PatternFly v6 token name.

The class follows the ng- prefix convention as outlined in the PR objectives. The --pf-t--global--text--color--subtle token is valid in PatternFly v6 and represents the semantic token for subtle text color used for secondary content.

src/components/PodStatus/PodStatus.tsx (2)

116-149: LGTM! Footer restructure and URL fixes look correct.

The migration to Content component="p" for paragraph blocks follows PatternFly v6 conventions. The URL paths now correctly use pod.metadata.name (fixing the previous typo mentioned in the summary).


3-3: PatternFly v6 migration from Text/TextContent to Content is correct.

The import change aligns with PatternFly v6 patterns. The Content component is the official replacement for Text, TextContent, and TextList components in PatternFly v6, as documented in the upgrade guide.

src/utils/units.ts (4)

189-208: LGTM!

The consistent use of Number(v) coercion across all wrapper functions is a clean approach to handle the updated type signature that accepts both string and number values.


223-236: LGTM!

The normalization logic correctly handles edge cases (-0, NaN, Infinity), and the ratio-to-percentage conversion via formatPercentage is accurate since Intl.NumberFormat with style: 'percent' multiplies by 100 internally.


238-249: LGTM!

The conversion chain is correct: percentage input → divide by 100 to get ratio → formatPercentage with style: 'percent' multiplies back. Edge case normalization is properly handled.


179-187: Verify SDK Humanize type compatibility.

The custom HumanizeFunc type accepts string | number, while humanizeRatio and humanizePercentage are typed as the SDK's Humanize. Confirm that the SDK's type signature is compatible with the internal Number() coercion pattern used here.

src/components/ClusterOverview/GPUStatus.tsx (2)

115-116: LGTM!

The CheckCircleIcon color prop correctly accepts the CSS variable string. Both usages are consistent with the PatternFly v6 migration pattern.

Also applies to: 150-151


18-19: Verify the fallback color matches PatternFly v6 token resolution.

The CSS variable token --pf-t--global--color--status--success--default is correct for PatternFly v6, but according to PatternFly documentation, this semantic token resolves to #3d7317 in the light theme, not #3e8635. Confirm whether the fallback color should be updated to #3d7317 to match the actual token value, or clarify if #3e8635 is intentional.

locales/en/plugin__console-plugin-nvidia-gpu.json (1)

6-85: LGTM!

The GPU-centric vocabulary reorganization looks well-structured. Keys are alphabetically sorted, terminology is consistent (GPU, Donut, Sparkline prefixes for metric variants), and the new keys align with the dashboard UI changes mentioned in the PR.

.dockerignore (1)

1-4: LGTM!

Standard and appropriate exclusions for a Node.js Docker build context. These patterns will reduce build context size and prevent unnecessary files from being copied into the image.

Dockerfile (2)

1-7: LGTM - Node.js 20 upgrade aligns with package.json requirements.

The base image upgrade to ubi9/nodejs-20 correctly matches the engines constraint in package.json. The conditional yarn installation and updated working directory paths are appropriate.


9-13: LGTM!

The final stage correctly copies artifacts from the updated build path, and the runtime configuration remains appropriate for the nginx-based serving approach.

webpack.config.ts (4)

7-10: LGTM - Clean production/development toggle.

The isProd flag provides a clean way to toggle between production and development configurations. The CommonJS require for copy-webpack-plugin with the eslint disable comment is appropriate given the module's export style.


43-44: LGTM - SCSS support added.

The addition of sass-loader aligns with the sass dependency in package.json and enables SCSS file processing alongside CSS.


53-58: LGTM - ESM compatibility fix.

Setting fullySpecified: false for .mjs/.js modules is a common workaround for ESM packages that don't fully specify their imports, which is often needed when consuming newer dependencies.


76-84: LGTM - Production optimizations.

The production configuration correctly enables deterministic chunk IDs (for long-term caching), minimization, and disables source maps. The locales path resolution using path.resolve is more robust than relative paths.

package.json (3)

5-8: LGTM - License and engine constraints added.

The Apache-2.0 license and node >= 20.0.0 engine constraint align with the PR objectives and Dockerfile base image upgrade.


84-86: Verify i18next peerDependency is appropriate.

Adding i18next as a peerDependency with the same version as devDependencies (^23.11.5) is correct if the plugin expects the host application to provide i18next at runtime. Ensure this aligns with OpenShift Console's provided dependencies.


22-27: Incorrect SDK version for OpenShift 4.19 compatibility.

PatternFly v6 upgrade is correct for OpenShift 4.19 (PF4 support removed). However, the SDK version 1.4.0 is for OpenShift Console 4.16.x, not 4.19. For OpenShift 4.19+ compatibility, the SDK version should be 4.19.x to match the Console minor version. Update the package.json accordingly.

Likely an incorrect or invalid review comment.

@empovit empovit force-pushed the update-for-ocp-4.19-no-chart-changes branch from 58fbe0d to 20e8d07 Compare January 13, 2026 18:16
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/PodStatus/PodStatus.tsx (1)

64-67: Potential null pointer dereference in waiting state handling.

When waiting && !waiting.reason is true but terminated is undefined (which is possible since waiting being truthy doesn't guarantee terminated exists), accessing terminated.signal and terminated.exitCode will throw a TypeError.

This appears to be a pre-existing bug, but it's worth fixing during this refactor.

🐛 Proposed fix
       } else if (waiting && !waiting.reason) {
-        phase = terminated.signal
-          ? `Signal:${terminated.signal}`
-          : `ExitCode:${terminated.exitCode}`;
+        phase = terminated
+          ? terminated.signal
+            ? `Signal:${terminated.signal}`
+            : `ExitCode:${terminated.exitCode}`
+          : 'Waiting';
       } else if (running && ready) {
🤖 Fix all issues with AI agents
In @.eslintrc.yml:
- Around line 1-26: Add a stricter TypeScript ESLint preset to the extends array
(for example "plugin:@typescript-eslint/recommended-requiring-type-checking" or
"plugin:@typescript-eslint/strict") and, if you choose the
requiring-type-checking preset, configure parserOptions.project to point to your
tsconfig (e.g., "parserOptions.project": "./tsconfig.json"); update the extends
list and parserOptions in the existing .eslintrc.yml (refer to the extends and
parserOptions keys) so type-aware rules are enabled and the linter can perform
type-checking.

In @package.json:
- Line 47: Remove the unused "prettier-stylelint" dependency from package.json
by deleting the "prettier-stylelint": "^0.4.2" entry (it may appear under
dependencies or devDependencies), then run your package manager (npm install or
yarn install) to update package-lock.json or yarn.lock accordingly; verify no
scripts reference "prettier-stylelint" and commit the updated package.json and
lockfile.
- Around line 24-27: The @patternfly/react-charts dependency is on v7 (
"^7.4.2") which is incompatible with the PatternFly 6 stack; update the
package.json dependency for "@patternfly/react-charts" to a PatternFly
6–compatible v8 release (e.g. "^8.0.0" or latest v8), then reinstall (npm/yarn
install) and verify peer dependencies resolve without warnings.
🧹 Nitpick comments (5)
src/components/PodStatus/PodStatus.tsx (1)

116-149: PatternFly v6 Content migration looks correct.

The migration from Text/TextContent to Content component="p" is appropriate for PF v6.

One minor improvement: the map at line 135 implicitly returns undefined for containers that don't match the condition, resulting in empty array slots. Consider using filter before map or returning null explicitly.

♻️ Optional: Use filter().map() pattern
          <Divider />
-          {containers.map((container) => {
-            if (isContainerCrashLoopBackOff(pod, container.name) && !isWindowsPod(pod)) {
-              return (
-                <div key={container.name}>
-                  <Link
-                    to={`/k8s/ns/${pod.metadata.namespace}/pods/${pod.metadata.name}/containers/${container.name}/debug`}
-                    data-test={`popup-debug-container-link-${container.name}`}
-                  >
-                    {t('Debug container {{name}}', { name: container.name })}
-                  </Link>
-                </div>
-              );
-            }
-          })}
+          {containers
+            .filter((container) => isContainerCrashLoopBackOff(pod, container.name) && !isWindowsPod(pod))
+            .map((container) => (
+              <div key={container.name}>
+                <Link
+                  to={`/k8s/ns/${pod.metadata.namespace}/pods/${pod.metadata.name}/containers/${container.name}/debug`}
+                  data-test={`popup-debug-container-link-${container.name}`}
+                >
+                  {t('Debug container {{name}}', { name: container.name })}
+                </Link>
+              </div>
+            ))}
src/components/GPUDashboard/GPUSelector.tsx (1)

47-56: Consider showing GPU model name in toggle for better UX.

The toggle currently displays the raw UUID (selectedGPU?.uuid), which may be long and not user-friendly. Consider displaying the model name instead:

💡 Suggested improvement
         <MenuToggle
           ref={toggleRef}
           onClick={() => setOpen(!isOpen)}
           isExpanded={isOpen}
           isDisabled={isDisabled}
         >
-          {selectedGPU?.uuid || t('Select GPU')}
+          {selectedGPU?.modelName || t('Select GPU')}
         </MenuToggle>
src/components/shared/OverviewDetailItem.tsx (1)

30-35: Consider simplifying className logic.

The inline ternary on line 33 works but could be cleaner with a template literal or extracted variable:

💅 Optional cleanup
+  const ddClassName = valueClassName
+    ? `ng-overview-detail-item__value ${valueClassName}`
+    : 'ng-overview-detail-item__value';
+
   return (
     <dl className="ng-overview-detail-item">
       <dt className="ng-overview-detail-item__title">{title}</dt>
-      <dd className={valueClassName ? `ng-overview-detail-item__value ${valueClassName}` : 'ng-overview-detail-item__value'}>{value}</dd>
+      <dd className={ddClassName}>{value}</dd>
     </dl>
   );
i18n-scripts/set-english-defaults.js (1)

8-60: Consider using consistent async/sync file operations.

The function mixes synchronous read (fs.readFileSync) with asynchronous write (fs.promises.writeFile). While functional, this is inconsistent. Since the caller uses await, consider using fs.promises.readFile for consistency.

♻️ Optional: Use async read for consistency
-function updateFile(fileName) {
-  let file;
-  try {
-    const fileContent = fs.readFileSync(fileName, 'utf8');
-    file = JSON.parse(fileContent);
-  } catch (err) {
-    throw new Error(`Failed to read or parse ${fileName}: ${err.message}`);
-  }
+async function updateFile(fileName) {
+  let file;
+  try {
+    const fileContent = await fs.promises.readFile(fileName, 'utf8');
+    file = JSON.parse(fileContent);
+  } catch (err) {
+    throw new Error(`Failed to read or parse ${fileName}: ${err.message}`);
+  }
src/utils/units.ts (1)

209-221: Minor edge case: Zero cores displays as "0m".

When numValue is 0 (after normalization), the condition numValue < 1 evaluates to true, resulting in unit = 'm'. This means zero cores will display as "0m" rather than "0".

If this is intentional for UI consistency with sub-core values, no action needed. Otherwise, consider:

♻️ Optional: Display zero without millicores unit
 export const humanizeCpuCores: HumanizeFunc = (v) => {
   let numValue = Number(v);
   if (!isFinite(numValue) || numValue === 0) {
     numValue = 0;
   }
-  const value = numValue < 1 ? round(numValue * 1000) : numValue;
-  const unit = numValue < 1 ? 'm' : '';
+  const value = numValue < 1 && numValue > 0 ? round(numValue * 1000) : numValue;
+  const unit = numValue < 1 && numValue > 0 ? 'm' : '';
   return {
     string: `${formatValue(value)}${unit}`,
     unit,
     value,
   };
 };
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 58fbe0d and 20e8d07.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (33)
  • .dockerignore
  • .eslintrc
  • .eslintrc.yml
  • .gitignore
  • .prettierrc.yml
  • .stylelintrc.yaml
  • Dockerfile
  • README.md
  • deployment/console-plugin-nvidia-gpu/Chart.yaml
  • i18n-scripts/build-i18n.sh
  • i18n-scripts/common.js
  • i18n-scripts/lexers.js
  • i18n-scripts/set-english-defaults.js
  • i18next-parser.config.js
  • locales/en/plugin__console-plugin-nvidia-gpu.json
  • package.json
  • src/components/ClusterOverview/GPUProviders.tsx
  • src/components/ClusterOverview/GPUStatus.tsx
  • src/components/GPUDashboard/Cards/InfoCard.tsx
  • src/components/GPUDashboard/Cards/MetricsCard.css
  • src/components/GPUDashboard/Cards/MetricsCard.tsx
  • src/components/GPUDashboard/Cards/WorkloadsCard.css
  • src/components/GPUDashboard/Cards/WorkloadsCard.tsx
  • src/components/GPUDashboard/GPUDashboard.tsx
  • src/components/GPUDashboard/GPUSelector.tsx
  • src/components/NotAvailable.css
  • src/components/NotAvailable.tsx
  • src/components/PodStatus/PodStatus.tsx
  • src/components/shared/OverviewDetailItem.css
  • src/components/shared/OverviewDetailItem.tsx
  • src/utils/units.ts
  • tsconfig.json
  • webpack.config.ts
💤 Files with no reviewable changes (2)
  • .eslintrc
  • .prettierrc.yml
🚧 Files skipped from review as they are similar to previous changes (16)
  • src/components/NotAvailable.css
  • src/components/GPUDashboard/Cards/MetricsCard.css
  • src/components/ClusterOverview/GPUProviders.tsx
  • src/components/GPUDashboard/Cards/MetricsCard.tsx
  • src/components/GPUDashboard/Cards/InfoCard.tsx
  • i18n-scripts/build-i18n.sh
  • .stylelintrc.yaml
  • deployment/console-plugin-nvidia-gpu/Chart.yaml
  • i18n-scripts/common.js
  • src/components/shared/OverviewDetailItem.css
  • src/components/NotAvailable.tsx
  • Dockerfile
  • tsconfig.json
  • src/components/GPUDashboard/Cards/WorkloadsCard.css
  • .dockerignore
  • i18n-scripts/lexers.js
🧰 Additional context used
🧬 Code graph analysis (2)
src/components/GPUDashboard/GPUDashboard.tsx (1)
src/components/GPUDashboard/Cards/GPUDashboardGraphs.tsx (1)
  • GPUDashboardGraphs (48-146)
webpack.config.ts (3)
i18next-parser.config.js (1)
  • require (2-2)
i18n-scripts/common.js (1)
  • path (2-2)
i18n-scripts/set-english-defaults.js (1)
  • path (2-2)
🔇 Additional comments (32)
src/components/PodStatus/PodStatus.tsx (4)

1-7: Imports updated correctly for PatternFly v6.

The migration from Text/TextContent/TextVariants to Content aligns with PatternFly v6's component consolidation.


9-81: No changes in podPhase function.


166-174: No changes in PodStatusPopover component.


176-186: No changes in helper functions.

.gitignore (1)

77-77: LGTM! Cache pattern appropriately extended.

The .cache_* wildcard pattern correctly extends coverage for build tool cache artifacts.

src/components/GPUDashboard/Cards/WorkloadsCard.tsx (2)

48-55: CSS class naming convention updated correctly.

The co-break-wordng-break-word rename aligns with the PR objective to follow template CSS naming conventions with the ng- prefix.


39-46: Pattern is correct for PatternFly v6.

The icon prop on EmptyState is the correct PatternFly v6 pattern, automatically wrapping the icon component internally. This properly replaces the v4 child component approach.

src/components/GPUDashboard/GPUDashboard.tsx (1)

24-57: Layout simplification looks good.

The removal of the outer Page wrapper and direct use of PageSection with Stack-based layout is cleaner. The component hierarchy is well-organized with appropriate PatternFly v6 layout components.

src/components/GPUDashboard/GPUSelector.tsx (2)

24-36: Disabled option handling is correct.

The isDisabled prop on SelectOption for loading and error states follows PatternFly v6 patterns correctly.


39-59: PatternFly v6 Select implementation is correct.

The migration properly uses the v6 API with the toggle renderer function pattern, correctly forwarding the toggleRef to the MenuToggle component. The props (isOpen, selected, onSelect, onOpenChange, SelectList) all match the current PatternFly v6 API.

src/components/shared/OverviewDetailItem.tsx (2)

6-12: Well-designed props interface.

The props type is clean and properly typed with appropriate optional fields for loading/error states.


21-28: Loading and error state handling is correct.

The priority order (loading → error → children) is sensible. The Spinner with size="md" is appropriate for inline loading indication.

src/components/ClusterOverview/GPUStatus.tsx (2)

115-116: LGTM!

The okColor string is correctly passed to the color prop. The migration from okColor.value (PatternFly v4 object export) to the direct CSS variable string is consistent across both health status icons.

Also applies to: 150-151


18-19: Migration to PatternFly v6 CSS variable is correct.

The token --pf-t--global--color--status--success--default is the correct semantic design token for success status colors in PatternFly v6. The CSS custom property approach with fallback is the proper pattern for PF6, and the implementation is sound.

README.md (1)

24-24: LGTM!

Clear documentation of OpenShift version compatibility, distinguishing between main branch (4.19+) and release branch (4.12-4.18) requirements. This aligns well with the PR's compatibility upgrade objectives.

webpack.config.ts (4)

61-73: Dev server security note: allowedHosts: 'all' is acceptable for local development only.

This configuration allows connections from any host, which is appropriate for the development workflow described in the README. The dev server should never be exposed in production environments.


7-8: LGTM!

The CommonJS require with eslint-disable is an acceptable workaround for copy-webpack-plugin's module format. The patterns syntax and path resolution are correct.

Also applies to: 74-79


10-10: Clean production/development configuration toggle.

The isProd pattern provides clear separation between build modes with appropriate optimizations: hashed filenames for cache busting, deterministic chunk IDs, and minification in production; readable names and source maps in development.

Also applies to: 17-17, 22-23, 80-84


42-58: LGTM!

SCSS support and ESM resolution configuration are correctly implemented. The fullySpecified: false rule addresses common ESM import issues in the webpack ecosystem.

i18next-parser.config.js (2)

6-13: Configuration aligns with the i18n workflow.

sort: true ensures reproducible locale files, and useKeysAsDefaultValue: true works in conjunction with the set-english-defaults.js script to handle pluralization patterns.


1-2: LGTM!

The lexers configuration is comprehensive, covering standard web file types with appropriate lexers. The CustomJSONLexer for JSON files enables specialized parsing that extracts translation keys matching the format %key% from locale files, which aligns with the i18n workflow improvements in this PR.

i18n-scripts/set-english-defaults.js (2)

27-53: Pluralization logic correctly handles i18next-parser 9.x format.

The branching logic properly distinguishes between {{count}} keys (for parameterized plurals) and simple _one/_other suffixes. The pluralize library usage is appropriate for generating English plural forms.


62-75: LGTM!

The execution flow correctly filters to process only the English locale and includes appropriate error handling with non-zero exit code on failure, ensuring build pipeline visibility.

locales/en/plugin__console-plugin-nvidia-gpu.json (1)

1-85: LGTM!

The locale file is well-organized with:

  • Alphabetically sorted keys (matching sort: true config)
  • Proper i18next pluralization patterns (_one/_other suffixes)
  • Consistent GPU-centric terminology throughout
  • Comprehensive coverage for UI elements (Donut charts, Sparklines, status messages)
package.json (4)

5-8: LGTM!

The license declaration and Node.js engine constraint align with the PR objectives for upgrading to Node.js 20.


9-19: LGTM!

The updated scripts properly support the TypeScript and tooling upgrades. The type-check script is a good addition for CI validation, and the webpack invocation via ts-node/register correctly enables TypeScript configuration support.


84-86: LGTM!

Declaring i18next as a peer dependency is correct for a console plugin, as the host application (OpenShift Console) provides the i18next instance. The version aligns with the devDependency for local development.


61-61: Verify webpack version pinning rationale with the development team.

Webpack is pinned to exact version 5.75.0 (released Nov 2022) while the current stable is 5.104.1 (Dec 2025). Although the commit message mentions fixing a security vulnerability, pinning to an older version may leave newer vulnerabilities unpatched. Clarify whether newer versions have breaking changes incompatible with the plugin, or if there's a documented reason this specific version was chosen. If possible, upgrade to the latest patched version to ensure comprehensive vulnerability coverage.

src/utils/units.ts (4)

179-187: LGTM!

The HumanizeFunc type signature update to accept string | number aligns well with the Number(v) coercion applied in the wrapper functions. This flexibility is useful when dealing with metrics APIs that may return string representations of numbers.


189-208: LGTM!

The Number(v) coercion is applied consistently across all wrapper functions. Non-finite values (NaN, Infinity) are correctly handled downstream in the humanize() function which normalizes them to 0.


238-249: LGTM!

The implementation correctly handles percentage input (0-100 range), dividing by 100 for formatPercentage which uses style: 'percent'. Edge case handling is consistent with humanizeRatio.


223-236: No type compatibility issues found.

The function correctly implements the SDK's Humanize type signature, which expects a single parameter. The implementation's numeric coercion and edge case handling are appropriate.

@empovit empovit force-pushed the update-for-ocp-4.19-no-chart-changes branch from 20e8d07 to 6972873 Compare January 13, 2026 18:34
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @package.json:
- Line 61: Update the webpack dependency in package.json from "webpack":
"5.75.0" to a secure 5.x release (e.g., "webpack": "5.94.0" or use a caret range
like "webpack": "^5.94.0") to ensure the CVE-2023-28154 and CVE-2024-43788 fixes
are included; after changing the "webpack" entry, run your package manager
(npm/yarn/pnpm) to reinstall and update lockfile accordingly and verify the
build passes.

In @src/components/GPUDashboard/GPUSelector.tsx:
- Around line 30-31: The SelectOption created when there are no GPUs (in the
branch that sets items when !gpus?.length || gpusError) currently only sets
value and isDisabled, so it renders empty; update that SelectOption (the element
in items) to include display text as children (e.g., t('No GPU found')) so the
option shows the message while keeping value and isDisabled intact.
🧹 Nitpick comments (4)
src/components/GPUDashboard/GPUSelector.tsx (1)

47-56: Consider displaying the GPU model name instead of UUID in the toggle.

The toggle currently shows selectedGPU?.uuid which displays a technical identifier to users. Displaying the model name would be more user-friendly.

💡 Suggested improvement
         <MenuToggle
           ref={toggleRef}
           onClick={() => setOpen(!isOpen)}
           isExpanded={isOpen}
           isDisabled={isDisabled}
         >
-          {selectedGPU?.uuid || t('Select GPU')}
+          {selectedGPU?.modelName || t('Select GPU')}
         </MenuToggle>
src/components/ClusterOverview/GPUProviders.tsx (1)

45-45: Consider removing or restoring the commented valueClassName prop.

The commented valueClassName="co-select-to-copy" appears to be leftover code. Either restore it if the copy-to-clipboard styling is needed, or remove the comment to reduce noise.

i18n-scripts/set-english-defaults.js (1)

8-60: Consider making file I/O consistently async.

The function uses synchronous fs.readFileSync (line 11) but asynchronous fs.promises.writeFile (line 59). This mixing works but is inconsistent. For better consistency and to avoid blocking the event loop on larger files:

♻️ Optional: Use async read
-function updateFile(fileName) {
-  let file;
-  try {
-    const fileContent = fs.readFileSync(fileName, 'utf8');
-    file = JSON.parse(fileContent);
-  } catch (err) {
-    throw new Error(`Failed to read or parse ${fileName}: ${err.message}`);
-  }
+async function updateFile(fileName) {
+  let file;
+  try {
+    const fileContent = await fs.promises.readFile(fileName, 'utf8');
+    file = JSON.parse(fileContent);
+  } catch (err) {
+    throw new Error(`Failed to read or parse ${fileName}: ${err.message}`);
+  }
package.json (1)

47-47: prettier-stylelint is deprecated and unmaintained.

This package hasn't been updated since 2017 and is no longer maintained. Consider removing it and running Prettier and Stylelint separately, which the lint script already does.

Suggested fix
-    "prettier-stylelint": "^0.4.2",
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 20e8d07 and 6972873.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (33)
  • .dockerignore
  • .eslintrc
  • .eslintrc.yml
  • .gitignore
  • .prettierrc.yml
  • .stylelintrc.yaml
  • Dockerfile
  • README.md
  • deployment/console-plugin-nvidia-gpu/Chart.yaml
  • i18n-scripts/build-i18n.sh
  • i18n-scripts/common.js
  • i18n-scripts/lexers.js
  • i18n-scripts/set-english-defaults.js
  • i18next-parser.config.js
  • locales/en/plugin__console-plugin-nvidia-gpu.json
  • package.json
  • src/components/ClusterOverview/GPUProviders.tsx
  • src/components/ClusterOverview/GPUStatus.tsx
  • src/components/GPUDashboard/Cards/InfoCard.tsx
  • src/components/GPUDashboard/Cards/MetricsCard.css
  • src/components/GPUDashboard/Cards/MetricsCard.tsx
  • src/components/GPUDashboard/Cards/WorkloadsCard.css
  • src/components/GPUDashboard/Cards/WorkloadsCard.tsx
  • src/components/GPUDashboard/GPUDashboard.tsx
  • src/components/GPUDashboard/GPUSelector.tsx
  • src/components/NotAvailable.css
  • src/components/NotAvailable.tsx
  • src/components/PodStatus/PodStatus.tsx
  • src/components/shared/OverviewDetailItem.css
  • src/components/shared/OverviewDetailItem.tsx
  • src/utils/units.ts
  • tsconfig.json
  • webpack.config.ts
💤 Files with no reviewable changes (2)
  • .prettierrc.yml
  • .eslintrc
✅ Files skipped from review due to trivial changes (1)
  • i18n-scripts/common.js
🚧 Files skipped from review as they are similar to previous changes (18)
  • src/components/PodStatus/PodStatus.tsx
  • i18n-scripts/lexers.js
  • src/components/GPUDashboard/Cards/MetricsCard.tsx
  • .stylelintrc.yaml
  • .gitignore
  • src/components/NotAvailable.tsx
  • Dockerfile
  • src/components/GPUDashboard/GPUDashboard.tsx
  • src/components/GPUDashboard/Cards/InfoCard.tsx
  • src/utils/units.ts
  • .eslintrc.yml
  • src/components/GPUDashboard/Cards/WorkloadsCard.css
  • i18n-scripts/build-i18n.sh
  • src/components/NotAvailable.css
  • src/components/ClusterOverview/GPUStatus.tsx
  • webpack.config.ts
  • i18next-parser.config.js
  • .dockerignore
🧰 Additional context used
🧬 Code graph analysis (1)
i18n-scripts/set-english-defaults.js (3)
i18n-scripts/common.js (2)
  • fs (1-1)
  • path (2-2)
i18next-parser.config.js (1)
  • require (2-2)
i18n-scripts/lexers.js (1)
  • keys (9-9)
🔇 Additional comments (22)
src/components/GPUDashboard/Cards/MetricsCard.css (1)

8-11: LGTM!

The new .ng-metrics-card__title class follows the ng- prefix convention established in this PR and correctly replaces the PatternFly utility class for center alignment.

src/components/GPUDashboard/GPUSelector.tsx (1)

39-59: LGTM!

The PatternFly v6 Select implementation correctly uses the controlled pattern with isOpen, selected, onSelect, and onOpenChange props. The MenuToggle integration and SelectList wrapper follow the updated API properly.

src/components/shared/OverviewDetailItem.tsx (1)

14-36: LGTM!

Clean implementation of a shared detail item component. The loading/error/content state handling is correct, and the dl/dt/dd structure is semantically appropriate for term-definition pairs. The component follows the ng- prefix convention for CSS classes.

src/components/ClusterOverview/GPUProviders.tsx (1)

2-2: LGTM!

The import path correctly references the new local OverviewDetailItem component, aligning with the PR's goal of removing the external @openshift-console/plugin-shared dependency.

src/components/shared/OverviewDetailItem.css (1)

1-22: LGTM!

Well-structured CSS following the ng- prefix convention. The PatternFly v6 design tokens (--pf-t--global--*) are correctly used for consistent theming, and the word-break properties ensure proper handling of long values.

src/components/GPUDashboard/Cards/WorkloadsCard.tsx (2)

48-55: LGTM!

CSS class rename from co-break-word to ng-break-word aligns with the template CSS naming conventions (ng- prefix) stated in the PR objectives. The utility class is correctly defined in the companion CSS file.


39-46: The EmptyState implementation is correct. PatternFly v6 accepts the icon prop with React components like CubesIcon directly, which matches your implementation. No changes needed.

README.md (1)

24-24: Documentation update looks good.

The prerequisites correctly reflect that the main branch now requires OpenShift 4.19+, while the release branch supports 4.12-4.18. This aligns with the Chart.yaml version bump to 0.3.0.

tsconfig.json (2)

10-12: Good TypeScript strictness improvements.

Enabling strict: true is the right approach as it subsumes noImplicitAny and strictNullChecks while adding additional checks. Adding skipLibCheck: true improves build performance without sacrificing type safety in your own code.


16-22: ts-node configuration supports the build tooling.

The ts-node block with module: "commonjs" allows TypeScript-based configuration files (like webpack.config.ts) and i18n scripts to run correctly in Node.js. Setting transpileOnly: true speeds up execution by skipping type checking during script runs.

deployment/console-plugin-nvidia-gpu/Chart.yaml (2)

4-4: Version requirement update is accurate.

The description correctly states the new 4.19+ requirement. Consider whether users on 4.12-4.18 should be directed to use an older chart version (e.g., 0.2.x) — this could be clarified in chart notes or the README's deployment section.


15-15: Version bump to 0.3.0 is appropriate.

The minor version bump correctly signals backward-incompatible changes (new OCP version requirement). This aligns with the PR objectives.

locales/en/plugin__console-plugin-nvidia-gpu.json (2)

2-5: Pluralization format follows i18next conventions.

The _one/_other suffix pattern with {{count}} interpolation is the correct i18next-parser 9.x format. The singular/plural values are properly defined.


1-85: Locale file structure looks good.

The keys are alphabetically sorted and cover the GPU dashboard terminology comprehensively. The file is well-organized with consistent naming patterns (Donut/Sparkline prefixes for chart labels, GPU-centric vocabulary).

i18n-scripts/set-english-defaults.js (3)

32-53: Pluralization logic correctly handles i18next-parser 9.x conventions.

The implementation properly distinguishes between:

  • Keys with {{count}} and _one suffix → singular form
  • Keys with {{count}} and _other suffix → plural form
  • Simple _one/_other suffixes → base key or pluralized base

This aligns with the locale file structure in plugin__console-plugin-nvidia-gpu.json.


62-66: Only English locale processing is intentional.

The filter for 'en' locale is appropriate since this script sets English defaults — other locales would need translation rather than auto-generated values.


68-75: IIFE with proper error handling.

The async IIFE correctly handles errors by logging and exiting with a non-zero status code, which is appropriate for build scripts.

package.json (5)

5-8: LGTM!

Adding the license field and Node.js engine constraint is good practice. The >=20.0.0 requirement aligns with the Dockerfile update mentioned in the PR objectives.


11-19: LGTM!

The script updates are well-structured:

  • Build scripts properly set NODE_ENV for production builds
  • The type-check script supports the TypeScript strict mode objective
  • The webpack wrapper using ts-node/register correctly handles the TypeScript webpack config

28-40: LGTM!

The TypeScript tooling versions are internally consistent. @types/node correctly matches the Node.js 20 engine requirement.


84-86: LGTM!

Declaring i18next as a peer dependency is correct for a plugin, ensuring the host console provides the i18n runtime. The version matches the devDependency for local development consistency.


24-27: No changes required. PatternFly v6.2.2 explicitly supports React 17, which is the version currently installed (^17.0.1). The dependencies are compatible.

Likely an incorrect or invalid review comment.

@empovit empovit force-pushed the update-for-ocp-4.19-no-chart-changes branch from 6972873 to cd77fe9 Compare January 13, 2026 18:52
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (6)
.dockerignore (1)

1-4: LGTM! Essential Docker ignore patterns are in place.

The file correctly excludes the most critical directories and files for a Node.js project. These patterns will optimize Docker build context size and prevent unnecessary files from being copied.

💡 Optional: Consider additional patterns for a more comprehensive exclusion list

While the current patterns are sufficient, you may optionally add:

 node_modules
 dist
 .git
 *.log
+.gitignore
+README.md
+*.md
+test
+tests
+**/*.test.js
+**/*.test.ts
+**/*.spec.js
+**/*.spec.ts
+.vscode
+.idea
+.DS_Store
+.env*
+coverage

These additional patterns can further reduce build context size and prevent development-only files from being included.

src/components/GPUDashboard/GPUSelector.tsx (1)

46-46: Variable shadowing reduces clarity.

The parameter isOpen shadows the state variable on line 18. Consider renaming for readability.

✨ Suggested fix
-      onOpenChange={(isOpen) => setOpen(isOpen)}
+      onOpenChange={(open) => setOpen(open)}
i18n-scripts/lexers.js (1)

24-27: Nit: Redundant reassignment in catch block.

The keys = [] assignment at line 26 is redundant since keys was already initialized as an empty array at line 9 and won't have been modified if parsing fails at the jsonc.parse call.

♻️ Optional simplification
     } catch (e) {
       console.error('Failed to parse as JSON.', filename, e);
-      keys = [];
     }
i18n-scripts/set-english-defaults.js (1)

8-60: Consider using consistent async I/O throughout.

The function mixes synchronous fs.readFileSync (line 11) with asynchronous fs.promises.writeFile (line 59). While functional, this inconsistency can be confusing and blocks the event loop during reads.

♻️ Suggested refactor for consistent async I/O
-function updateFile(fileName) {
-  let file;
-  try {
-    const fileContent = fs.readFileSync(fileName, 'utf8');
-    file = JSON.parse(fileContent);
-  } catch (err) {
-    throw new Error(`Failed to read or parse ${fileName}: ${err.message}`);
-  }
+async function updateFile(fileName) {
+  let file;
+  try {
+    const fileContent = await fs.promises.readFile(fileName, 'utf8');
+    file = JSON.parse(fileContent);
+  } catch (err) {
+    throw new Error(`Failed to read or parse ${fileName}: ${err.message}`);
+  }
webpack.config.ts (1)

61-73: Note: Permissive dev server settings.

allowedHosts: 'all' and the wildcard CORS headers are fine for local development but could pose a risk if the dev server is accidentally exposed on a shared network. Consider documenting that this configuration is for local development only.

src/utils/units.ts (1)

209-221: Minor inconsistency in rounding behavior.

Two observations:

  1. Zero displays as "0m": When numValue is 0, the condition numValue < 1 is true, so unit = 'm'. This results in "0m" rather than "0".

  2. Inconsistent rounding of returned value: For numValue < 1, the returned value is rounded (line 214), but for numValue >= 1, it's returned as-is. The display is fine (handled by formatValue), but consumers relying on the value property see inconsistent precision.

If this matches the previous behavior, no action needed; otherwise consider:

Optional fix for consistency
 export const humanizeCpuCores: HumanizeFunc = (v) => {
   let numValue = Number(v);
   if (!isFinite(numValue) || numValue === 0) {
     numValue = 0;
   }
-  const value = numValue < 1 ? round(numValue * 1000) : numValue;
+  const value = numValue < 1 ? round(numValue * 1000) : round(numValue);
   const unit = numValue < 1 ? 'm' : '';
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6972873 and cd77fe9.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (33)
  • .dockerignore
  • .eslintrc
  • .eslintrc.yml
  • .gitignore
  • .prettierrc.yml
  • .stylelintrc.yaml
  • Dockerfile
  • README.md
  • deployment/console-plugin-nvidia-gpu/Chart.yaml
  • i18n-scripts/build-i18n.sh
  • i18n-scripts/common.js
  • i18n-scripts/lexers.js
  • i18n-scripts/set-english-defaults.js
  • i18next-parser.config.js
  • locales/en/plugin__console-plugin-nvidia-gpu.json
  • package.json
  • src/components/ClusterOverview/GPUProviders.tsx
  • src/components/ClusterOverview/GPUStatus.tsx
  • src/components/GPUDashboard/Cards/InfoCard.tsx
  • src/components/GPUDashboard/Cards/MetricsCard.css
  • src/components/GPUDashboard/Cards/MetricsCard.tsx
  • src/components/GPUDashboard/Cards/WorkloadsCard.css
  • src/components/GPUDashboard/Cards/WorkloadsCard.tsx
  • src/components/GPUDashboard/GPUDashboard.tsx
  • src/components/GPUDashboard/GPUSelector.tsx
  • src/components/NotAvailable.css
  • src/components/NotAvailable.tsx
  • src/components/PodStatus/PodStatus.tsx
  • src/components/shared/OverviewDetailItem.css
  • src/components/shared/OverviewDetailItem.tsx
  • src/utils/units.ts
  • tsconfig.json
  • webpack.config.ts
💤 Files with no reviewable changes (2)
  • .prettierrc.yml
  • .eslintrc
🚧 Files skipped from review as they are similar to previous changes (15)
  • src/components/NotAvailable.css
  • src/components/NotAvailable.tsx
  • i18n-scripts/common.js
  • src/components/PodStatus/PodStatus.tsx
  • Dockerfile
  • .gitignore
  • README.md
  • deployment/console-plugin-nvidia-gpu/Chart.yaml
  • src/components/shared/OverviewDetailItem.tsx
  • tsconfig.json
  • src/components/GPUDashboard/Cards/WorkloadsCard.css
  • src/components/GPUDashboard/GPUDashboard.tsx
  • package.json
  • src/components/ClusterOverview/GPUStatus.tsx
  • src/components/shared/OverviewDetailItem.css
🧰 Additional context used
🧬 Code graph analysis (1)
i18n-scripts/lexers.js (2)
i18next-parser.config.js (1)
  • require (2-2)
i18n-scripts/set-english-defaults.js (2)
  • keys (22-22)
  • key (25-25)
🔇 Additional comments (29)
src/components/GPUDashboard/Cards/WorkloadsCard.tsx (2)

39-46: LGTM! Correct PatternFly v6 migration for EmptyState.

The icon prop on EmptyState is the correct API for PatternFly v6, replacing the deprecated EmptyStateIcon component pattern from v4.


48-55: LGTM! CSS class naming follows plugin conventions.

Renaming from co-break-word (Console internal) to ng-break-word (plugin namespace) correctly decouples the plugin from OpenShift Console's internal CSS classes, aligning with the PR's CSS naming convention objectives.

src/components/GPUDashboard/Cards/MetricsCard.css (1)

8-11: LGTM!

The new .ng-metrics-card__title class correctly replaces the PatternFly utility class pf-u-text-align-center, following the ng- prefix convention and maintaining consistent BEM-like naming with sibling selectors in this file.

src/components/GPUDashboard/Cards/MetricsCard.tsx (1)

95-95: LGTM!

The class name change from pf-u-text-align-center to ng-metrics-card__title correctly aligns with the PatternFly v6 migration and the project's ng- prefix naming convention. The corresponding CSS definition is properly imported from ./MetricsCard.css.

src/components/ClusterOverview/GPUProviders.tsx (1)

2-2: Verify import change and local component implementation.

The import path was changed from @openshift-console/plugin-shared to ../shared/OverviewDetailItem. Confirm that:

  • The local OverviewDetailItem component exists at src/components/shared/OverviewDetailItem.tsx
  • It exports the same interface (accepting title, isLoading, error, children props)
  • All usages across the codebase are compatible with this change
src/components/GPUDashboard/GPUSelector.tsx (4)

2-10: LGTM on PatternFly v6 migration.

The imports correctly reflect the PF v6 Select pattern using SelectList, MenuToggle, and MenuToggleElement. This aligns with the PR objective of upgrading to PatternFly v6.


14-20: Disabled state logic is correct.

The isDisabled boolean properly accounts for loading, error, and empty GPU states.


22-36: LGTM on items generation.

The type annotation React.ReactElement<SelectOptionProps>[] is appropriately specific, and the isDisabled prop usage aligns with PF v6 patterns.


54-54: Consider displaying a user-friendly GPU identifier.

The toggle shows the raw UUID, which may be long and not user-friendly. If modelName is available on the GPU object (as suggested by its use elsewhere), consider displaying it instead—or a combination if multiple GPUs of the same model might exist.

.eslintrc.yml (1)

1-25: LGTM!

The ESLint configuration is well-structured with appropriate extends for TypeScript and React projects. Key highlights:

  • Prettier integration as error ensures consistent formatting
  • react/prop-types: off is correct since TypeScript handles prop validation
  • React version detection avoids manual version maintenance
i18n-scripts/build-i18n.sh (1)

1-7: LGTM!

The script follows shell best practices:

  • set -exuo pipefail ensures proper error handling and debugging output
  • File pattern correctly covers all relevant source types including JSON for the custom lexer
  • Output path template properly escapes $LOCALE and $NAMESPACE for i18next interpretation
i18n-scripts/lexers.js (1)

7-29: LGTM!

The custom lexer implementation is correct:

  • Extends EventEmitter as required by i18next-parser for custom lexers
  • Uses comment-json to support JSON files with comments
  • Gracefully handles parse errors without breaking the build
.stylelintrc.yaml (1)

1-26: LGTM!

Excellent Stylelint configuration for a console plugin:

  • Dark mode support enforced by disallowing named/hex colors and rgb() in favor of PatternFly CSS variables
  • Reserved prefixes (pf-, co-) properly blocked to avoid conflicts with PatternFly and OpenShift console styles
  • Element selector restrictions with appropriate exceptions prevent unintended global style impacts
  • Clear inline comments document the rationale for each rule
i18next-parser.config.js (1)

1-31: LGTM!

The i18next-parser configuration is correctly set up:

  • CustomJSONLexer properly imported and registered for JSON file parsing
  • Built-in lexers correctly referenced by string names while custom lexer is passed as class reference (this is the expected pattern for i18next-parser)
  • sort: true ensures consistent ordering in locale files
  • Comprehensive lexer mappings cover all relevant file types in the project
i18n-scripts/set-english-defaults.js (3)

1-6: LGTM on imports and constants.

Clean setup with necessary dependencies for file I/O and pluralization handling.


62-66: LGTM!

The filter logic correctly targets only the English locale folder for processing.


68-75: LGTM!

Proper error handling with informative logging and appropriate exit code for build script integration.

webpack.config.ts (5)

7-10: LGTM on the environment-based configuration setup.

The CommonJS require for CopyWebpackPlugin with the eslint-disable comment is a reasonable workaround for TypeScript/ESM interop. The isProd flag provides clean conditional logic throughout.


16-24: Well-structured output configuration.

Hash-based filenames in production enable effective cache busting, while development retains readable names for debugging. Good practice.


42-58: Good additions for SCSS and ESM compatibility.

The loader chain order is correct for SCSS processing, and fullySpecified: false addresses common ESM interop issues with packages that omit file extensions.


74-79: LGTM!

The CopyWebpackPlugin configuration correctly resolves the locales directory and copies it to the output. This integrates well with the i18n tooling pipeline.


80-84: Solid optimization settings.

Disabling source maps in production, using deterministic chunk IDs for caching, and conditional minimization are all best practices for production builds.

locales/en/plugin__console-plugin-nvidia-gpu.json (3)

2-5: LGTM on pluralization keys.

The _one and _other suffixes follow i18next-parser 9.x conventions correctly.


14-21: Well-organized chart component keys.

The Donut and Sparkline prefixed keys provide clear categorization for different visualization components. The naming convention is consistent throughout.

Also applies to: 70-77


1-85: LGTM!

The locale file is well-structured with comprehensive GPU-related vocabulary. Keys are alphabetically sorted and follow i18next conventions. The file integrates properly with the new i18n tooling pipeline.

src/components/GPUDashboard/Cards/InfoCard.tsx (1)

3-3: Verify the local OverviewDetailItem component implementation.

The import path change from @openshift-console/plugin-shared to the local ../../shared/OverviewDetailItem is consistent with the SDK upgrade pattern. However, the local component implementation at src/components/shared/OverviewDetailItem.tsx needs manual verification to ensure it properly exports the same interface and behavior as the original library component, given that @openshift-console/plugin-shared no longer exports it in SDK 1.4.0.

src/utils/units.ts (3)

179-187: LGTM – Type widening to string | number is appropriate.

This change provides flexibility for callers passing string values (common with Prometheus metric results) while the internal Number() coercion ensures type safety downstream.


189-208: LGTM – Consistent numeric coercion across all wrappers.

The Number(v) coercion is uniformly applied, and downstream humanize() already handles non-finite values at lines 147-149.


223-249: Logic is correct; verify SDK Humanize type compatibility.

The numeric coercion and normalization logic is sound. The distinction between humanizeRatio (input: 0-1) and humanizePercentage (input: 0-100) is correctly preserved.

However, these functions now use the SDK's Humanize type instead of the local HumanizeFunc. Since the SDK upgraded from 0.0.11 to 1.4.0, please verify that the Humanize type signature is compatible with the single-parameter functions here.

OpenShift dynamic-plugin-sdk Humanize type signature 1.4.0

@empovit empovit force-pushed the update-for-ocp-4.19-no-chart-changes branch from cd77fe9 to c3712f1 Compare January 14, 2026 05:45
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@package.json`:
- Around line 86-88: package.json currently lists i18next in both "dependencies"
and "peerDependencies", which is redundant and can cause duplicate instances;
decide whether the host should provide i18next or the package should bundle it:
if the host supplies it for module federation, remove i18next from
"dependencies" and keep it in "peerDependencies"; if this package should bundle
i18next, remove it from "peerDependencies" and keep it in "dependencies"; also
evaluate whether react-i18next should be moved to "peerDependencies" (or added
there alongside i18next) to enforce a single shared instance in host
applications and avoid version conflicts.
♻️ Duplicate comments (1)
package.json (1)

63-63: Acknowledged: webpack 5.75.0 pinned due to SDK compatibility.

Per past discussions, this version is required by @openshift-console/[email protected]. The security vulnerabilities (CVE-2023-28154, CVE-2024-43788) remain a concern but cannot be addressed until the SDK is updated.

🧹 Nitpick comments (6)
i18n-scripts/common.js (1)

14-27: Simplify async function wrapper.

The IIFE wrapper around the async function is unnecessary. A direct async function is cleaner and equivalent.

♻️ Suggested simplification
-  parseFolder(directory, argFunction, packageDir) {
-    return (async () => {
-      try {
-        const files = await fs.promises.readdir(directory);
-        for (const file of files) {
-          const filePath = path.join(directory, file);
-          await argFunction(filePath, packageDir);
-        }
-      } catch (e) {
-        console.error(`Failed to parseFolder ${directory}:`, e);
-        throw e;
-      }
-    })();
-  },
+  async parseFolder(directory, argFunction, packageDir) {
+    try {
+      const files = await fs.promises.readdir(directory);
+      for (const file of files) {
+        const filePath = path.join(directory, file);
+        await argFunction(filePath, packageDir);
+      }
+    } catch (e) {
+      console.error(`Failed to parseFolder ${directory}:`, e);
+      throw e;
+    }
+  },
i18n-scripts/set-english-defaults.js (1)

59-60: Consider adding async keyword for clarity.

The function returns a Promise but isn't marked async. While functional, marking it async would make the intent clearer and enable await usage within the function if needed later.

♻️ Suggested change
-function updateFile(fileName) {
+async function updateFile(fileName) {
   // ... existing code ...
-  return fs.promises.writeFile(fileName, JSON.stringify(updatedFile, null, 2));
+  await fs.promises.writeFile(fileName, JSON.stringify(updatedFile, null, 2));
 }
package.json (2)

62-64: TypeScript 4.9.5 is outdated but acceptable given ecosystem constraints.

TypeScript 5.x offers improved type inference and performance, but staying on 4.9.5 maintains compatibility with the current tooling stack. Consider upgrading when SDK constraints allow.


35-36: Consider upgrading TypeScript ESLint packages.

@typescript-eslint/eslint-plugin and @typescript-eslint/parser at ^5.14.0 are significantly outdated. The current stable is v8.42.0, which offers improved TypeScript support. Upgrading requires ESLint ^8.57.0 or higher (not 9.x specifically)—you can stay on the 8.x line if needed by bumping ESLint from ^8.10.0 to ^8.57.0+.

locales/en/plugin__console-plugin-nvidia-gpu.json (2)

14-21: Consider whether "Donut" prefix adds value for translators.

Keys like "Donut GPU utilization" and "Donut Encoder utilization" appear to be chart-type identifiers. If these are purely for accessibility labels (aria-labels), they may confuse translators. Consider adding translator comments or using a more descriptive pattern.


70-77: Same consideration for "Sparkline" prefixed keys.

Similar to the "Donut" keys, "Sparkline" prefixed entries could benefit from translator context. These appear to be accessibility labels for chart components.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cd77fe9 and c3712f1.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (33)
  • .dockerignore
  • .eslintrc
  • .eslintrc.yml
  • .gitignore
  • .prettierrc.yml
  • .stylelintrc.yaml
  • Dockerfile
  • README.md
  • deployment/console-plugin-nvidia-gpu/Chart.yaml
  • i18n-scripts/build-i18n.sh
  • i18n-scripts/common.js
  • i18n-scripts/lexers.js
  • i18n-scripts/set-english-defaults.js
  • i18next-parser.config.js
  • locales/en/plugin__console-plugin-nvidia-gpu.json
  • package.json
  • src/components/ClusterOverview/GPUProviders.tsx
  • src/components/ClusterOverview/GPUStatus.tsx
  • src/components/GPUDashboard/Cards/InfoCard.tsx
  • src/components/GPUDashboard/Cards/MetricsCard.css
  • src/components/GPUDashboard/Cards/MetricsCard.tsx
  • src/components/GPUDashboard/Cards/WorkloadsCard.css
  • src/components/GPUDashboard/Cards/WorkloadsCard.tsx
  • src/components/GPUDashboard/GPUDashboard.tsx
  • src/components/GPUDashboard/GPUSelector.tsx
  • src/components/NotAvailable.css
  • src/components/NotAvailable.tsx
  • src/components/PodStatus/PodStatus.tsx
  • src/components/shared/OverviewDetailItem.css
  • src/components/shared/OverviewDetailItem.tsx
  • src/utils/units.ts
  • tsconfig.json
  • webpack.config.ts
💤 Files with no reviewable changes (2)
  • .prettierrc.yml
  • .eslintrc
🚧 Files skipped from review as they are similar to previous changes (12)
  • src/components/GPUDashboard/GPUDashboard.tsx
  • src/components/NotAvailable.css
  • src/components/GPUDashboard/Cards/MetricsCard.css
  • .stylelintrc.yaml
  • src/components/shared/OverviewDetailItem.css
  • src/components/shared/OverviewDetailItem.tsx
  • i18n-scripts/build-i18n.sh
  • .eslintrc.yml
  • src/components/GPUDashboard/Cards/InfoCard.tsx
  • src/components/GPUDashboard/Cards/MetricsCard.tsx
  • deployment/console-plugin-nvidia-gpu/Chart.yaml
  • .dockerignore
🧰 Additional context used
🧬 Code graph analysis (4)
i18n-scripts/common.js (2)
i18n-scripts/set-english-defaults.js (3)
  • fs (1-1)
  • path (2-2)
  • file (9-9)
i18next-parser.config.js (1)
  • require (2-2)
i18n-scripts/set-english-defaults.js (3)
i18n-scripts/common.js (2)
  • fs (1-1)
  • path (2-2)
i18next-parser.config.js (1)
  • require (2-2)
i18n-scripts/lexers.js (1)
  • keys (9-9)
src/components/NotAvailable.tsx (1)
src/i18n.ts (1)
  • useTranslation (15-17)
i18n-scripts/lexers.js (2)
i18next-parser.config.js (1)
  • require (2-2)
i18n-scripts/set-english-defaults.js (2)
  • keys (22-22)
  • key (25-25)
🔇 Additional comments (40)
src/components/NotAvailable.tsx (1)

4-8: LGTM!

The migration from PatternFly utility class (pf-u-color-200) to a local component-scoped CSS class (ng-not-available) aligns well with the PR's stated objectives for the PatternFly v6 upgrade and following the ng- prefix naming convention. The functional behavior is preserved.

src/components/ClusterOverview/GPUStatus.tsx (2)

18-19: LGTM!

The CSS variable approach with the correct PatternFly v6 fallback (#3e8635) is a clean solution for the migration. This provides proper theming support while ensuring graceful degradation.


115-116: Consistent usage of the new color constant.

Both health check functions correctly apply the okColor constant for success states, maintaining visual consistency across the component.

Also applies to: 150-151

src/components/PodStatus/PodStatus.tsx (2)

3-3: LGTM — PatternFly v6 import migration.

The Content import correctly replaces the deprecated Text/TextContent/TextVariants components from PatternFly v4.


116-149: LGTM — PatternFly v6 migration and URL path fix.

The migration from Text/TextContent to Content with component="p" is correct for PatternFly v6. The fragment wrapper is appropriate here. Additionally, the URL paths now correctly reference pod.metadata.name (fixing what appears to have been a typo).

src/components/ClusterOverview/GPUProviders.tsx (1)

2-2: LGTM — Import updated to use local component.

This change connects GPUProviders.tsx to the locally managed OverviewDetailItem implementation, which is appropriate given the Console SDK upgrade from 0.0.11 to 1.4.0. The component interface perfectly matches the props being passed: title, isLoading, error, and children. The valueClassName prop is available in the local component but is simply commented out in this specific usage.

src/utils/units.ts (5)

179-187: LGTM!

The type broadening to accept string | number improves flexibility for handling API responses that may return numeric values as strings, while maintaining a consistent number return type.


189-208: LGTM!

Consistent Number(v) coercion across all wrapper functions aligns with the updated type signature. Non-finite values (including NaN from invalid strings) are properly normalized to 0 by the central humanize() function.


209-221: LGTM!

The numeric coercion and normalization logic correctly handles edge cases. The "0m" output for zero values is retained per previous discussion.


223-236: LGTM!

Proper numeric coercion and normalization. Using the SDK's Humanize type improves consistency with the dynamic plugin ecosystem. The fraction digits calculation correctly scales the ratio for percentage-appropriate precision.


238-249: LGTM!

Consistent numeric handling with appropriate scaling (dividing by 100) since this function expects percentage-scale input (0-100) rather than ratio-scale (0-1). The SDK Humanize type alignment is a good consistency improvement.

src/components/GPUDashboard/Cards/WorkloadsCard.css (1)

6-9: LGTM!

The new .ng-break-word class correctly implements word-breaking behavior for table cells. Using both word-break: break-word and overflow-wrap: anywhere ensures robust text wrapping across browsers for long pod names and namespaces. The ng- prefix aligns with the template CSS naming conventions.

src/components/GPUDashboard/Cards/WorkloadsCard.tsx (2)

48-55: LGTM!

The className migration from co-break-word to the locally-defined ng-break-word correctly removes the dependency on OpenShift Console global styles. This aligns with the PR objective to follow template CSS naming conventions and makes the component self-contained.


39-46: EmptyState icon prop usage is correct for PatternFly v6.

The migration from the PF v4 EmptyStateIcon wrapper to the v6 icon prop is implemented correctly. The prop accepts a React component reference (like CubesIcon), which is then automatically wrapped by the internal EmptyStateIcon component.

.gitignore (1)

75-77: LGTM!

The addition of .cache_* appropriately extends the existing .cache ignore pattern to cover cache directories with suffixes, which aligns with the build tooling updates in this PR.

tsconfig.json (1)

7-22: LGTM!

The TypeScript configuration updates are well-aligned with the PR objectives:

  • strict: true enables comprehensive type checking.
  • sourceMap: true improves debugging experience.
  • The ts-node block with transpileOnly: true and module: commonjs enables efficient execution of TypeScript scripts (e.g., the i18n tooling) without full type-checking overhead.
README.md (1)

24-24: LGTM!

The prerequisite update correctly documents the OpenShift 4.19+ requirement for the main branch while preserving backward compatibility information for the release branch.

i18next-parser.config.js (1)

1-31: LGTM!

The i18next-parser configuration is well-structured:

  • sort: true ensures deterministic, reproducible locale catalogs.
  • The lexers mapping comprehensively covers the project's file types.
  • CustomJSONLexer is correctly passed as a class reference for custom lexer handling.

The eslint-disable comments are appropriate for this CommonJS configuration file.

i18n-scripts/lexers.js (1)

7-29: LGTM!

The CustomJSONLexer correctly implements the i18next-parser lexer interface. The regex pattern /^%(.+)%$/ appropriately captures localization keys in the %key% format, and the error handling gracefully degrades by returning an empty array on parse failures. The comment-json dependency is properly declared in package.json.

Dockerfile (2)

1-7: LGTM! Build stage properly upgraded to Node.js 20.

The upgrade from ubi8/nodejs-16 to ubi9/nodejs-20 aligns with the PR objectives. The conditional yarn installation (command -v yarn || npm i -g yarn) is a good defensive pattern.


9-13: LGTM! Runtime stage correctly configured.

The final stage properly references the renamed build stage (--from=build), and USER 1001 ensures the container runs as a non-root user for security.

src/components/GPUDashboard/GPUSelector.tsx (3)

2-10: LGTM! PatternFly v6 Select imports correctly updated.

The imports align with PF v6's Select component API, including MenuToggle for the custom toggle renderer and SelectList for the options container.


24-41: LGTM! Select options properly structured with display text.

The loading and "No GPU found" options now correctly include children text for display, and the GPU options use uuid as both value and display with model name as description.


45-65: PatternFly v6 Select API usage is correct.

The controlled Select implementation properly uses isOpen, selected, onSelect, and onOpenChange props with correct signatures. The MenuToggle toggle renderer follows the expected pattern.

i18n-scripts/common.js (2)

4-13: LGTM! Defensive error handling for directory check.

The isDirectory helper properly catches errors from lstatSync (e.g., path doesn't exist) and returns false, which is appropriate for a predicate function.


28-35: LGTM! Sync delete with proper error handling.

The deleteFile helper logs the error before rethrowing, which aids debugging.

webpack.config.ts (4)

7-10: LGTM! Production/development configuration flag.

Using isProd to conditionally configure webpack is a clean pattern. The CommonJS require for copy-webpack-plugin is acceptable given the eslint-disable comment.


42-58: LGTM! SCSS and ESM support added.

Adding sass-loader enables SCSS compilation for the new styling changes. The fullySpecified: false rule for .mjs/.js files is necessary for proper ESM module resolution in webpack 5.


61-73: Note: allowedHosts: 'all' is permissive.

This setting allows connections from any host to the dev server, which is convenient for development but bypasses host header checks. This is acceptable for local development but ensure this configuration isn't used in any publicly accessible environment.


74-84: LGTM! Production optimizations properly configured.

The deterministic chunk IDs and minification in production, combined with named chunks and source maps in development, provide optimal builds for each environment.

i18n-scripts/set-english-defaults.js (3)

8-19: LGTM! JSON file reading with proper validation.

The implementation correctly uses fs.readFileSync (addressing the previous caching concern) and validates the parsed content is a non-array object before processing.


24-57: LGTM! Pluralization transformation logic is comprehensive.

The key transformation handles all i18next-parser 9.x pluralization cases:

  • {{count}} ..._one → singular form
  • {{count}} ..._other → plural form
  • Simple _one/_other suffixes
  • Default fallback using key as value

68-75: LGTM! Entry point with proper error handling.

The IIFE properly catches errors and exits with a non-zero status code, which is appropriate for a build script that should fail the pipeline on error.

package.json (5)

5-8: LGTM! License and engine requirements properly specified.

Adding the Apache-2.0 license and requiring Node.js >=20.0.0 aligns with the PR objectives for upgrading to Node.js 20.


11-19: LGTM! Scripts modernized appropriately.

The build scripts now use a cleaner webpack invocation pattern, and the i18n pipeline is properly wired to the new extraction scripts. The addition of type-check script supports the TypeScript strict mode enablement mentioned in PR objectives.


22-24: Good documentation of version constraints.

The JSON comments clearly explain why webpack and react-charts are pinned, addressing the SDK compatibility constraints. This helps future maintainers understand the dependencies.


25-30: PatternFly v6 packages aligned, with documented exception for react-charts.

The SDK and PatternFly core/icons/table packages are updated to v6 as intended. The react-charts v7 pinning is documented and explained in past discussions (module federation incompatibility with v8).


50-54: React 17 with React Router 5 is appropriate for SDK compatibility.

The pinned React 17 and React Router 5.3.x versions align with the OpenShift Console SDK requirements for module federation compatibility.

locales/en/plugin__console-plugin-nvidia-gpu.json (2)

2-5: LGTM! Pluralization values correctly implemented.

The plural keys now have proper display values without the suffix repetition. The _one variants use singular forms and _other variants use plural forms as expected by i18next.


1-85: LGTM! GPU-centric vocabulary is comprehensive and well-structured.

The locale file covers the expected UI elements for GPU monitoring: metrics, status indicators, actions, and vendor names. Keys are alphabetically sorted and follow consistent naming conventions.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@empovit empovit force-pushed the update-for-ocp-4.19-no-chart-changes branch from c3712f1 to e518398 Compare January 14, 2026 05:56
@empovit
Copy link
Member Author

empovit commented Jan 14, 2026

@coderabbitai full review

@coderabbitai
Copy link

coderabbitai bot commented Jan 14, 2026

✅ Actions performed

Full review triggered.

@empovit empovit changed the title [WIP] Upgrade plugin for OpenShift 4.19+ compatibility Upgrade plugin for OpenShift 4.19+ compatibility Jan 14, 2026
@empovit
Copy link
Member Author

empovit commented Jan 14, 2026

This was tested on 4.19 (HCP cluster with three workers, one GPU each) and 4.20 (single node, single GPU).

- Update Console SDK 0.0.11 → 1.4.0
- Upgrade PatternFly v4 → v6
- Upgrade to Node.js 20
- Enable TypeScript strict mode
- Update CSS and UI controls
- Bump Helm chart version to 0.3.0 (backward compatible)
- Update dependencies
@empovit empovit force-pushed the update-for-ocp-4.19-no-chart-changes branch from e518398 to 831f050 Compare January 14, 2026 10:02
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/PodStatus/PodStatus.tsx (1)

64-67: Potential null reference in existing logic.

If waiting is truthy and !waiting.reason is true, but terminated is undefined (which is possible since the if chain only checks waiting), accessing terminated.signal on line 65 will throw a TypeError.

This appears to be pre-existing code, but worth flagging during this review since it could cause runtime crashes.

🐛 Proposed fix
       } else if (waiting && !waiting.reason) {
-        phase = terminated.signal
+        phase = terminated?.signal
           ? `Signal:${terminated.signal}`
-          : `ExitCode:${terminated.exitCode}`;
+          : `ExitCode:${terminated?.exitCode}`;
♻️ Duplicate comments (1)
package.json (1)

59-59: Acknowledged: webpack 5.75.0 pinned for SDK compatibility.

As noted in prior discussion, webpack cannot be upgraded due to compatibility constraints with @openshift-console/[email protected]. However, this version has known CVEs (CVE-2023-28154, CVE-2024-43788). Consider documenting this constraint and the associated risk in a DEPENDENCIES.md or similar file for future maintainers.

🧹 Nitpick comments (5)
src/utils/units.ts (1)

264-266: Minor: Typo in comment.

Line 266 has "perent" instead of "percent".

Proposed fix
-    { style: 'percent' }, // Don't allow perent style to be overridden.
+    { style: 'percent' }, // Don't allow percent style to be overridden.
DEPENDENCIES.md (1)

18-20: Add language specifier to fenced code block.

The error message code block is missing a language identifier, which triggers markdownlint MD040.

📝 Suggested fix
-```
+```text
 TypeError: The 'compilation' argument must be an instance of Compilation
</details>

</blockquote></details>
<details>
<summary>src/components/shared/OverviewDetailItem.tsx (1)</summary><blockquote>

`33-33`: **Consider using template literals for className concatenation.**

Minor readability improvement for the className logic.

<details>
<summary>♻️ Optional refactor</summary>

```diff
-      <dd className={valueClassName ? `ng-overview-detail-item__value ${valueClassName}` : 'ng-overview-detail-item__value'}>{value}</dd>
+      <dd className={`ng-overview-detail-item__value${valueClassName ? ` ${valueClassName}` : ''}`}>{value}</dd>

Or use a utility like clsx if already available in the project:

<dd className={clsx('ng-overview-detail-item__value', valueClassName)}>{value}</dd>
i18next-parser.config.js (1)

1-2: Minor: Consider using consistent eslint-disable syntax.

The two eslint-disable comments can be consolidated for cleaner code.

Suggested consolidation
-// eslint-disable-next-line `@typescript-eslint/no-var-requires`, no-undef
-const { CustomJSONLexer } = require('./i18n-scripts/lexers');
-
-// eslint-disable-next-line no-undef
+/* eslint-disable `@typescript-eslint/no-var-requires`, no-undef */
+const { CustomJSONLexer } = require('./i18n-scripts/lexers');
+

This uses a single block-level disable at the top since no-undef applies to the entire file (for module.exports).

i18n-scripts/common.js (1)

14-27: Consider simplifying the async function pattern.

The IIFE pattern return (async () => { ... })() is functional but unnecessarily complex. An async function directly would be cleaner and more readable.

Suggested simplification
-  parseFolder(directory, argFunction, packageDir) {
-    return (async () => {
-      try {
-        const files = await fs.promises.readdir(directory);
-        for (const file of files) {
-          const filePath = path.join(directory, file);
-          await argFunction(filePath, packageDir);
-        }
-      } catch (e) {
-        console.error(`Failed to parseFolder ${directory}:`, e);
-        throw e;
-      }
-    })();
-  },
+  async parseFolder(directory, argFunction, packageDir) {
+    try {
+      const files = await fs.promises.readdir(directory);
+      for (const file of files) {
+        const filePath = path.join(directory, file);
+        await argFunction(filePath, packageDir);
+      }
+    } catch (e) {
+      console.error(`Failed to parseFolder ${directory}:`, e);
+      throw e;
+    }
+  },
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e518398 and 831f050.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (34)
  • .dockerignore
  • .eslintrc
  • .eslintrc.yml
  • .gitignore
  • .prettierrc.yml
  • .stylelintrc.yaml
  • DEPENDENCIES.md
  • Dockerfile
  • README.md
  • deployment/console-plugin-nvidia-gpu/Chart.yaml
  • i18n-scripts/build-i18n.sh
  • i18n-scripts/common.js
  • i18n-scripts/lexers.js
  • i18n-scripts/set-english-defaults.js
  • i18next-parser.config.js
  • locales/en/plugin__console-plugin-nvidia-gpu.json
  • package.json
  • src/components/ClusterOverview/GPUProviders.tsx
  • src/components/ClusterOverview/GPUStatus.tsx
  • src/components/GPUDashboard/Cards/InfoCard.tsx
  • src/components/GPUDashboard/Cards/MetricsCard.css
  • src/components/GPUDashboard/Cards/MetricsCard.tsx
  • src/components/GPUDashboard/Cards/WorkloadsCard.css
  • src/components/GPUDashboard/Cards/WorkloadsCard.tsx
  • src/components/GPUDashboard/GPUDashboard.tsx
  • src/components/GPUDashboard/GPUSelector.tsx
  • src/components/NotAvailable.css
  • src/components/NotAvailable.tsx
  • src/components/PodStatus/PodStatus.tsx
  • src/components/shared/OverviewDetailItem.css
  • src/components/shared/OverviewDetailItem.tsx
  • src/utils/units.ts
  • tsconfig.json
  • webpack.config.ts
💤 Files with no reviewable changes (2)
  • .prettierrc.yml
  • .eslintrc
🚧 Files skipped from review as they are similar to previous changes (20)
  • src/components/NotAvailable.css
  • src/components/GPUDashboard/Cards/MetricsCard.css
  • src/components/NotAvailable.tsx
  • src/components/ClusterOverview/GPUStatus.tsx
  • src/components/GPUDashboard/Cards/MetricsCard.tsx
  • src/components/GPUDashboard/GPUSelector.tsx
  • .gitignore
  • i18n-scripts/set-english-defaults.js
  • deployment/console-plugin-nvidia-gpu/Chart.yaml
  • .dockerignore
  • .eslintrc.yml
  • tsconfig.json
  • i18n-scripts/lexers.js
  • src/components/shared/OverviewDetailItem.css
  • Dockerfile
  • src/components/GPUDashboard/Cards/InfoCard.tsx
  • .stylelintrc.yaml
  • i18n-scripts/build-i18n.sh
  • src/components/GPUDashboard/Cards/WorkloadsCard.tsx
  • README.md
🧰 Additional context used
🧬 Code graph analysis (2)
src/components/GPUDashboard/GPUDashboard.tsx (1)
src/components/GPUDashboard/Cards/GPUDashboardGraphs.tsx (1)
  • GPUDashboardGraphs (48-146)
i18n-scripts/common.js (2)
i18n-scripts/set-english-defaults.js (3)
  • fs (1-1)
  • path (2-2)
  • file (9-9)
i18next-parser.config.js (1)
  • require (2-2)
🪛 markdownlint-cli2 (0.18.1)
DEPENDENCIES.md

30-30: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (19)
src/components/GPUDashboard/Cards/WorkloadsCard.css (1)

1-9: LGTM!

The missing closing brace fix and the new .ng-break-word utility class look good. The class correctly replaces the removed co-break-word dependency, and the naming follows the project's ng- prefix convention.

Note: word-break: break-word is technically deprecated, but it serves as a reasonable fallback for older browsers alongside the modern overflow-wrap: anywhere. This is fine to keep as-is.

src/utils/units.ts (4)

179-187: LGTM!

The type signature update to accept string | number aligns well with the SDK upgrade and provides flexibility for callers passing string values from API responses while maintaining type safety through explicit Number() coercion in the implementations.


189-208: LGTM!

Consistent Number(v) coercion across all wrapper functions. The downstream humanize() function already handles non-finite values (line 147-149), so invalid string inputs like "abc" will safely normalize to 0.


209-221: LGTM!

The coercion and normalization logic is correct. Input handling is consistent with other functions in this file.


223-249: LGTM!

The coercion pattern is consistent with the rest of the file. The semantic distinction between humanizeRatio (input 0.5 → "50%") and humanizePercentage (input 50 → "50%") is correctly preserved.

src/components/GPUDashboard/GPUDashboard.tsx (1)

26-55: LGTM! Clean PatternFly v6 layout structure.

The simplified layout using a single PageSection with Stack, Split, and Grid components is well-organized and follows PatternFly v6 patterns. The responsive grid layout for MIGCard and InfoCard is appropriate.

DEPENDENCIES.md (1)

1-40: Valuable dependency documentation.

This documentation will help future maintainers understand the version constraints and their rationale. The clear structure with "Reason", "Impact", and "Resolution" sections is excellent.

src/components/PodStatus/PodStatus.tsx (1)

116-149: Clean PatternFly v6 migration for footer content.

The refactor from Text/TextVariants to Content components with component="p" is correct for PatternFly v6. The fragment wrapper and structure maintain proper semantics.

webpack.config.ts (3)

61-73: DevServer configuration is appropriate for plugin development.

The allowedHosts: 'all' and broad CORS headers are acceptable for development since this configuration only applies to webpack-dev-server and is necessary for local testing with the OpenShift console. These settings do not affect production builds.


43-44: The SCSS loader configuration is properly supported by existing devDependencies.

All required packages (sass-loader@^13.3.0, sass@^1.77.0, css-loader@^6.7.1, and style-loader@^3.3.1) are already present in devDependencies. The loader chain ordering is correct.


76-78: Locales directory path is correct.

The locales directory exists at the project root with proper structure (containing the en subdirectory), and path.resolve(__dirname, 'locales') correctly resolves to it. The CopyWebpackPlugin configuration is valid.

src/components/shared/OverviewDetailItem.tsx (1)

14-36: Clean implementation of the shared detail item component.

The component correctly handles loading, error, and content states. The use of semantic dl/dt/dd elements is appropriate for definition list semantics. The error styling issue from the previous review has been addressed with the dedicated CSS class.

src/components/ClusterOverview/GPUProviders.tsx (1)

2-2: LGTM!

The import path update from @openshift-console/plugin-shared to the local shared component aligns with the SDK upgrade. This properly decouples the component from the external SDK dependency.

package.json (3)

5-8: LGTM!

Good additions - explicit Apache-2.0 license and Node.js 20+ engine requirement align with the PR objectives for OpenShift 4.19+ compatibility.


9-19: LGTM!

The script updates are well-structured:

  • The webpack helper script properly uses ts-node/register for TypeScript config support
  • The type-check script enables TypeScript strict mode validation
  • The i18n workflow integrates the new shell-based extraction pipeline

82-84: LGTM!

The i18next peer dependency is correctly specified. This ensures the host application provides the i18next instance for proper module federation sharing.

locales/en/plugin__console-plugin-nvidia-gpu.json (1)

1-85: LGTM!

The locale file correctly implements i18next pluralization. The _one/_other suffixes are properly used in keys while values contain the actual display text (e.g., "{{count}} GPU pod_one": "{{count}} GPU pod"). The GPU-centric terminology migration is comprehensive and consistent.

i18next-parser.config.js (1)

15-30: LGTM!

The lexers configuration correctly maps file extensions to their appropriate parsers. The CustomJSONLexer is properly passed as a class reference (not a string) which is the correct pattern for custom lexers in i18next-parser.

i18n-scripts/common.js (1)

4-36: LGTM overall - utility functions are well-structured.

The error handling is consistent across all three utilities, and the module exports provide a clean API for the i18n scripts. The synchronous/asynchronous choices are appropriate for their use cases.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@empovit
Copy link
Member Author

empovit commented Jan 14, 2026

Fixes #71 and #122

@empovit
Copy link
Member Author

empovit commented Jan 14, 2026

/cc @yakovbeder @MarkusGersdorf

@MarkusGersdorf
Copy link
Collaborator

/lgtm

@openshift-ci openshift-ci bot added the lgtm label Jan 14, 2026
@openshift-merge-bot openshift-merge-bot bot merged commit 63fa766 into rh-ecosystem-edge:main Jan 14, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants